From 6dda63a545a8402af7aef574dda2b03917d0cfa2 Mon Sep 17 00:00:00 2001 From: Shaunak Kishore Date: Wed, 17 Mar 2021 18:09:45 -0700 Subject: [PATCH] Add IR ops and helpers to make struct arrays Summary: Add an IR op to allocate and return a new struct array with a given layout. In the future we may want some variant of AllocStructDict for bespoke structs, but it's not needed right now. Reviewed By: colavitam Differential Revision: D27132096 fbshipit-source-id: ad341dfa48690b34b2a855f990009fe93514f110 --- hphp/doc/ir.specification | 26 +++++++++++------ hphp/runtime/base/bespoke/struct-array.cpp | 30 ++++++++++++++++++++ hphp/runtime/base/bespoke/struct-array.h | 11 ++++++++ hphp/runtime/vm/jit/dce.cpp | 2 ++ hphp/runtime/vm/jit/extra-data.cpp | 12 ++++++++ hphp/runtime/vm/jit/extra-data.h | 33 ++++++++++++++++++++++ hphp/runtime/vm/jit/ir-opcode.cpp | 2 ++ hphp/runtime/vm/jit/irlower-bespoke.cpp | 45 ++++++++++++++++++++++++++++++ hphp/runtime/vm/jit/memory-effects.cpp | 2 ++ 9 files changed, 154 insertions(+), 9 deletions(-) diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index 038ec5170ee..2f7aa111827 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -1497,7 +1497,7 @@ To string conversions: array. Used to initialize an array allocated with AllocVArray or AllocVec that was too big to use a series of InitVecElem ops. -| AllocStructDArray, DDArr, NA, PRc +| AllocStructDArray, DDArr, NA, PRc | AllocStructDict, D(Dict), NA, PRc @@ -1512,14 +1512,7 @@ To string conversions: S1 has already been inc-reffed. Used to initialize an array allocated by AllocStructArray / AllocStructDArray / AllocStructDict. -| NewRecord, D(Record), S(RecDesc) S(StkPtr), PRc|CRc - - Allocate a new record, given the type S0 and N immediate keys - and taking N elements off the stack given by S1, at `offset'. - This instruction assumes it can take the values from the stack - without increfing them. - -| NewStructDArray, DDArr, S(StkPtr), PRc|CRc +| NewStructDArray, DDArr, S(StkPtr), PRc|CRc | NewStructDict, D(Dict), S(StkPtr), PRc|CRc @@ -1527,6 +1520,21 @@ To string conversions: immediates for keys and N stack elements at `offset` on stack S0 for values. This op assumes it can take values from the stack without inc-reffing them. +| NewBespokeStructDArray, DDArr, S(StkPtr), PRc|CRc + +| NewBespokeStructDict, D(Dict), S(StkPtr), PRc|CRc + + Allocate a new dict or darray with the bespoke StructLayout `layout`. + Then, initialize the N fields with the given `slots` with N stack elements + starting at `offset`. This op moves stack values with no refcounting. + +| NewRecord, D(Record), S(RecDesc) S(StkPtr), PRc|CRc + + Allocate a new record, given the type S0 and N immediate keys + and taking N elements off the stack given by S1, at `offset'. + This instruction assumes it can take the values from the stack + without increfing them. + | NewCol, DCol, NA, PRc Create an empty new collection of `type'. `type' cannot be Pair. diff --git a/hphp/runtime/base/bespoke/struct-array.cpp b/hphp/runtime/base/bespoke/struct-array.cpp index a905b3f6cc0..cf0a11b62be 100644 --- a/hphp/runtime/base/bespoke/struct-array.cpp +++ b/hphp/runtime/base/bespoke/struct-array.cpp @@ -212,6 +212,36 @@ StructArray* StructArray::MakeFromVanilla(ArrayData* ad, return result; } +StructArray* StructArray::MakeStructDArray( + const StructLayout* layout, uint32_t size, + const Slot* slots, const TypedValue* vals) { + return MakeStructImpl(layout, size, slots, vals, HeaderKind::Mixed); +} + +StructArray* StructArray::MakeStructDict( + const StructLayout* layout, uint32_t size, + const Slot* slots, const TypedValue* vals) { + return MakeStructImpl(layout, size, slots, vals, HeaderKind::Dict); +} + +StructArray* StructArray::MakeStructImpl( + const StructLayout* layout, uint32_t size, + const Slot* slots, const TypedValue* vals, HeaderKind hk) { + + auto const result = MakeReserve(hk, false, layout); + auto const data = result->rawData(); + result->m_size = size; + + for (auto i = 0; i < size; i++) { + assertx(slots[i] <= layout->numFields()); + tvCopy(vals[i], &data[slots[i]]); + } + + assertx(result->checkInvariants()); + assertx(result->layout() == layout); + return result; +} + const StructLayout* StructArray::layout() const { return StructLayout::As(Layout::FromIndex(layoutIndex())); } diff --git a/hphp/runtime/base/bespoke/struct-array.h b/hphp/runtime/base/bespoke/struct-array.h index 7f442e4b6c6..716e7536794 100644 --- a/hphp/runtime/base/bespoke/struct-array.h +++ b/hphp/runtime/base/bespoke/struct-array.h @@ -39,6 +39,13 @@ struct StructArray : public BespokeArray { static StructArray* MakeReserve( HeaderKind kind, bool legacy, const StructLayout* layout); + static StructArray* MakeStructDArray( + const StructLayout* layout, uint32_t size, + const Slot* slots, const TypedValue* vals); + static StructArray* MakeStructDict( + const StructLayout* layout, uint32_t size, + const Slot* slots, const TypedValue* vals); + uint8_t sizeIndex() const; static size_t sizeFromLayout(const StructLayout*); @@ -52,6 +59,10 @@ struct StructArray : public BespokeArray { #undef X private: + static StructArray* MakeStructImpl( + const StructLayout* layout, uint32_t size, + const Slot* slots, const TypedValue* vals, HeaderKind hk); + const StructLayout* layout() const; const TypedValue* rawData() const; TypedValue* rawData(); diff --git a/hphp/runtime/vm/jit/dce.cpp b/hphp/runtime/vm/jit/dce.cpp index de5ed5d22dc..c6dc3f511dd 100644 --- a/hphp/runtime/vm/jit/dce.cpp +++ b/hphp/runtime/vm/jit/dce.cpp @@ -453,6 +453,8 @@ bool canDCE(IRInstruction* inst) { case NewRecord: case NewStructDArray: case NewStructDict: + case NewBespokeStructDArray: + case NewBespokeStructDict: case Clone: case InlineReturn: case InlineCall: diff --git a/hphp/runtime/vm/jit/extra-data.cpp b/hphp/runtime/vm/jit/extra-data.cpp index 79a41cf8cec..7beacb3a9e0 100644 --- a/hphp/runtime/vm/jit/extra-data.cpp +++ b/hphp/runtime/vm/jit/extra-data.cpp @@ -44,6 +44,18 @@ std::string NewStructData::show() const { return os.str(); } +std::string NewBespokeStructData::show() const { + std::ostringstream os; + os << layout.describe() << ','; + os << offset.offset << ",("; + auto delimiter = ""; + for (auto i = 0; i < numSlots; i++) { + os << delimiter << slots[i]; + delimiter = ","; + } + return os.str(); +} + ////////////////////////////////////////////////////////////////////// namespace { diff --git a/hphp/runtime/vm/jit/extra-data.h b/hphp/runtime/vm/jit/extra-data.h index 088f657ba08..d06e9eb93e0 100644 --- a/hphp/runtime/vm/jit/extra-data.h +++ b/hphp/runtime/vm/jit/extra-data.h @@ -1543,6 +1543,37 @@ struct NewStructData : IRExtraData { StringData** keys; }; +struct NewBespokeStructData : IRExtraData { + std::string show() const; + + size_t stableHash() const { + auto hash = folly::hash::hash_combine( + std::hash()(layout.toUint16()), + std::hash()(offset.offset), + std::hash()(numSlots) + ); + for (auto i = 0; i < numSlots; i++) { + hash = folly::hash::hash_combine(hash, slots[i]); + } + return hash; + } + + bool equals(const NewBespokeStructData& o) const { + if (layout != o.layout) return false; + if (offset != o.offset) return false; + if (numSlots != o.numSlots) return false; + for (auto i = 0; i < numSlots; i++) { + if (slots[i] != o.slots[i]) return false; + } + return true; + } + + ArrayLayout layout; + IRSPRelOffset offset; + uint32_t numSlots; + Slot* slots; +}; + struct PackedArrayData : IRExtraData { explicit PackedArrayData(uint32_t size) : size(size) {} std::string show() const { return folly::format("{}", size).str(); } @@ -2612,6 +2643,8 @@ X(NewStructDict, NewStructData); X(NewRecord, NewStructData); X(AllocStructDArray, NewStructData); X(AllocStructDict, NewStructData); +X(NewBespokeStructDArray, NewBespokeStructData); +X(NewBespokeStructDict, NewBespokeStructData); X(AllocVArray, PackedArrayData); X(AllocVec, PackedArrayData); X(NewKeysetArray, NewKeysetArrayData); diff --git a/hphp/runtime/vm/jit/ir-opcode.cpp b/hphp/runtime/vm/jit/ir-opcode.cpp index 5554a521a99..8664ce04147 100644 --- a/hphp/runtime/vm/jit/ir-opcode.cpp +++ b/hphp/runtime/vm/jit/ir-opcode.cpp @@ -861,6 +861,8 @@ bool opcodeMayRaise(Opcode opc) { case NewRFunc: case NewStructDArray: case NewStructDict: + case NewBespokeStructDArray: + case NewBespokeStructDict: case NInstanceOfBitmask: case Nop: case NSameObj: diff --git a/hphp/runtime/vm/jit/irlower-bespoke.cpp b/hphp/runtime/vm/jit/irlower-bespoke.cpp index 9b8b72a94da..ffaae128611 100644 --- a/hphp/runtime/vm/jit/irlower-bespoke.cpp +++ b/hphp/runtime/vm/jit/irlower-bespoke.cpp @@ -21,6 +21,7 @@ #include "hphp/runtime/base/bespoke/logging-profile.h" #include "hphp/runtime/base/bespoke/monotype-dict.h" #include "hphp/runtime/base/bespoke/monotype-vec.h" +#include "hphp/runtime/base/bespoke/struct-array.h" #include "hphp/runtime/base/mixed-array.h" #include "hphp/runtime/base/packed-array.h" #include "hphp/runtime/base/set-array.h" @@ -30,10 +31,12 @@ #include "hphp/runtime/vm/jit/irlower-internal.h" #include "hphp/runtime/vm/jit/ir-opcode.h" #include "hphp/runtime/vm/jit/minstr-helpers.h" +#include "hphp/runtime/vm/jit/translator-inline.h" namespace HPHP { namespace jit { namespace irlower { ////////////////////////////////////////////////////////////////////////////// +// Generic BespokeArrays namespace { static void logGuardFailure(TypedValue tv, uint16_t layout, uint64_t sk) { @@ -288,6 +291,7 @@ void cgBespokeElem(IRLS& env, const IRInstruction* inst) { } ////////////////////////////////////////////////////////////////////////////// +// MonotypeVec and MonotypeDict namespace { using MonotypeDict = bespoke::MonotypeDict; @@ -378,5 +382,46 @@ void cgLdMonotypeVecElem(IRLS& env, const IRInstruction* inst) { } ////////////////////////////////////////////////////////////////////////////// +// StructArray + +namespace { + +using bespoke::StructArray; +using bespoke::StructLayout; + +void newStructImpl( + IRLS& env, + const IRInstruction* inst, + StructArray* (*f)(const StructLayout* layout, uint32_t size, + const Slot* slots, const TypedValue* vals) +) { + auto const sp = srcLoc(env, inst, 0).reg(); + auto const extra = inst->extra(); + auto& v = vmain(env); + + auto const table = v.allocData(extra->numSlots); + memcpy(table, extra->slots, extra->numSlots * sizeof(*extra->slots)); + + auto const args = argGroup(env, inst) + .imm(StructLayout::As(extra->layout.bespokeLayout())) + .imm(extra->numSlots) + .dataPtr(table) + .addr(sp, cellsToBytes(extra->offset.offset)); + + cgCallHelper(v, env, CallSpec::direct(f), callDest(env, inst), + SyncOptions::None, args); +} + +} + +void cgNewBespokeStructDArray(IRLS& env, const IRInstruction* inst) { + newStructImpl(env, inst, StructArray::MakeStructDArray); +} + +void cgNewBespokeStructDict(IRLS& env, const IRInstruction* inst) { + newStructImpl(env, inst, StructArray::MakeStructDict); +} + +////////////////////////////////////////////////////////////////////////////// }}} diff --git a/hphp/runtime/vm/jit/memory-effects.cpp b/hphp/runtime/vm/jit/memory-effects.cpp index ec5e240b4aa..2380743f54b 100644 --- a/hphp/runtime/vm/jit/memory-effects.cpp +++ b/hphp/runtime/vm/jit/memory-effects.cpp @@ -1112,6 +1112,8 @@ MemEffects memory_effects_impl(const IRInstruction& inst) { case NewStructDArray: case NewStructDict: + case NewBespokeStructDArray: + case NewBespokeStructDict: { // NewStruct{Dict,DArray} is reading elements from the stack, but writes // to a completely new array, so we can treat the store set as empty. -- 2.11.4.GIT