Replace the fake ParseMessage/ParseGroup function calls in with ones that accept a lambda. It allows the callers to drop the mock "message" types that only exist to have an _InternalParse function in them. It also gives more freedom to the caller to do things like force inlining when it matters.

Do more inlining in certain callers of ParseLoop to avoid the extra stack frame.
This reduces the overall cost of maintaining the stack frames in functions like FastMtS1.

PiperOrigin-RevId: 590943926
pull/15056/head
Protobuf Team Bot 12 months ago committed by Copybara-Service
parent 34908e2898
commit f0a8c474cc
  1. 3
      src/google/protobuf/generated_message_tctable_impl.h
  2. 48
      src/google/protobuf/generated_message_tctable_lite.cc
  3. 76
      src/google/protobuf/parse_context.h

@ -394,6 +394,9 @@ class PROTOBUF_EXPORT TcParser final {
static const char* ParseLoop(MessageLite* msg, const char* ptr, static const char* ParseLoop(MessageLite* msg, const char* ptr,
ParseContext* ctx, ParseContext* ctx,
const TcParseTableBase* table); const TcParseTableBase* table);
static const char* ParseLoopInlined(MessageLite* msg, const char* ptr,
ParseContext* ctx,
const TcParseTableBase* table);
// Functions referenced by generated fast tables (numeric types): // Functions referenced by generated fast tables (numeric types):
// F: fixed V: varint Z: zigzag // F: fixed V: varint Z: zigzag

@ -73,7 +73,7 @@ const char* TcParser::GenericFallbackLite(PROTOBUF_TC_PARAM_DECL) {
// Core fast parsing implementation: // Core fast parsing implementation:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
PROTOBUF_NOINLINE const char* TcParser::ParseLoop( inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ParseLoopInlined(
MessageLite* msg, const char* ptr, ParseContext* ctx, MessageLite* msg, const char* ptr, ParseContext* ctx,
const TcParseTableBase* table) { const TcParseTableBase* table) {
// Note: TagDispatch uses a dispatch table at "&table->fast_entries". // Note: TagDispatch uses a dispatch table at "&table->fast_entries".
@ -98,6 +98,12 @@ PROTOBUF_NOINLINE const char* TcParser::ParseLoop(
return ptr; return ptr;
} }
PROTOBUF_NOINLINE const char* TcParser::ParseLoop(
MessageLite* msg, const char* ptr, ParseContext* ctx,
const TcParseTableBase* table) {
return ParseLoopInlined(msg, ptr, ctx, table);
}
// On the fast path, a (matching) 1-byte tag already has the decoded value. // On the fast path, a (matching) 1-byte tag already has the decoded value.
static uint32_t FastDecodeTag(uint8_t coded_tag) { static uint32_t FastDecodeTag(uint8_t coded_tag) {
return coded_tag; return coded_tag;
@ -387,11 +393,12 @@ inline PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularParseMessageAuxImpl(
if (field == nullptr) { if (field == nullptr) {
field = inner_table->default_instance->New(msg->GetArena()); field = inner_table->default_instance->New(msg->GetArena());
} }
if (group_coding) { const auto inner_loop = [&](const char* ptr) {
return ctx->ParseGroup<TcParser>(field, ptr, FastDecodeTag(saved_tag), return ParseLoopInlined(field, ptr, ctx, inner_table);
inner_table); };
} return group_coding ? ctx->ParseGroupInlined(ptr, FastDecodeTag(saved_tag),
return ctx->ParseMessage<TcParser>(field, ptr, inner_table); inner_loop)
: ctx->ParseLengthDelimitedInlined(ptr, inner_loop);
} else { } else {
if (field == nullptr) { if (field == nullptr) {
const MessageLite* default_instance = const MessageLite* default_instance =
@ -474,12 +481,12 @@ inline PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedParseMessageAuxImpl(
ptr += sizeof(TagType); ptr += sizeof(TagType);
MessageLite* submsg = field.AddMessage(default_instance); MessageLite* submsg = field.AddMessage(default_instance);
if (aux_is_table) { if (aux_is_table) {
if (group_coding) { const auto inner_loop = [&](const char* ptr) {
ptr = ctx->ParseGroup<TcParser>(submsg, ptr, return ParseLoopInlined(submsg, ptr, ctx, aux.table);
FastDecodeTag(expected_tag), aux.table); };
} else { ptr = group_coding ? ctx->ParseGroupInlined(
ptr = ctx->ParseMessage<TcParser>(submsg, ptr, aux.table); ptr, FastDecodeTag(expected_tag), inner_loop)
} : ctx->ParseLengthDelimitedInlined(ptr, inner_loop);
} else { } else {
if (group_coding) { if (group_coding) {
ptr = ctx->ParseGroup(submsg, ptr, FastDecodeTag(expected_tag)); ptr = ctx->ParseGroup(submsg, ptr, FastDecodeTag(expected_tag));
@ -2369,10 +2376,11 @@ PROTOBUF_NOINLINE const char* TcParser::MpMessage(PROTOBUF_TC_PARAM_DECL) {
if (need_init || field == nullptr) { if (need_init || field == nullptr) {
field = inner_table->default_instance->New(msg->GetArena()); field = inner_table->default_instance->New(msg->GetArena());
} }
if (is_group) { const auto inner_loop = [&](const char* ptr) {
return ctx->ParseGroup<TcParser>(field, ptr, decoded_tag, inner_table); return ParseLoop(field, ptr, ctx, inner_table);
} };
return ctx->ParseMessage<TcParser>(field, ptr, inner_table); return is_group ? ctx->ParseGroupInlined(ptr, decoded_tag, inner_loop)
: ctx->ParseLengthDelimitedInlined(ptr, inner_loop);
} else { } else {
if (need_init || field == nullptr) { if (need_init || field == nullptr) {
const MessageLite* def; const MessageLite* def;
@ -2428,9 +2436,11 @@ const char* TcParser::MpRepeatedMessageOrGroup(PROTOBUF_TC_PARAM_DECL) {
uint32_t next_tag; uint32_t next_tag;
do { do {
MessageLite* value = field.AddMessage(default_instance); MessageLite* value = field.AddMessage(default_instance);
ptr = is_group ? ctx->ParseGroup<TcParser>(value, ptr2, decoded_tag, const auto inner_loop = [&](const char* ptr) {
inner_table) return ParseLoop(value, ptr, ctx, inner_table);
: ctx->ParseMessage<TcParser>(value, ptr2, inner_table); };
ptr = is_group ? ctx->ParseGroupInlined(ptr2, decoded_tag, inner_loop)
: ctx->ParseLengthDelimitedInlined(ptr2, inner_loop);
if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) goto error; if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) goto error;
if (PROTOBUF_PREDICT_FALSE(!ctx->DataAvailable(ptr))) goto parse_loop; if (PROTOBUF_PREDICT_FALSE(!ctx->DataAvailable(ptr))) goto parse_loop;
ptr2 = ReadTag(ptr, &next_tag); ptr2 = ReadTag(ptr, &next_tag);

@ -494,34 +494,18 @@ class PROTOBUF_EXPORT ParseContext : public EpsCopyInputStream {
const char* ParseMessage(MessageLite* msg, const char* ptr); const char* ParseMessage(MessageLite* msg, const char* ptr);
// This overload supports those few cases where ParseMessage is called
// on a class that is not actually a proto message.
// TODO: Eliminate this use case.
template <typename T,
typename std::enable_if<!std::is_base_of<MessageLite, T>::value,
bool>::type = true>
PROTOBUF_NODISCARD const char* ParseMessage(T* msg, const char* ptr);
// Read the length prefix, push the new limit, call the func(ptr), and then // Read the length prefix, push the new limit, call the func(ptr), and then
// pop the limit. Useful for situations that don't value an actual message, // pop the limit. Useful for situations that don't have an actual message.
// like map entries.
template <typename Func> template <typename Func>
PROTOBUF_NODISCARD const char* ParseLengthDelimitedInlined(const char*, PROTOBUF_NODISCARD const char* ParseLengthDelimitedInlined(const char*,
const Func& func); const Func& func);
template <typename TcParser, typename Table> // Push the recursion depth, call the func(ptr), and then pop depth. Useful
PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE const char* ParseMessage( // for situations that don't have an actual message.
MessageLite* msg, const char* ptr, const Table* table) { template <typename Func>
LimitToken old; PROTOBUF_NODISCARD const char* ParseGroupInlined(const char* ptr,
ptr = ReadSizeAndPushLimitAndDepthInlined(ptr, &old); uint32_t start_tag,
if (ptr == nullptr) return ptr; const Func& func);
auto old_depth = depth_;
ptr = TcParser::ParseLoop(msg, ptr, this, table);
if (ptr != nullptr) ABSL_DCHECK_EQ(old_depth, depth_);
depth_++;
if (!PopLimit(std::move(old))) return nullptr;
return ptr;
}
template <typename T> template <typename T>
PROTOBUF_NODISCARD PROTOBUF_NDEBUG_INLINE const char* ParseGroup( PROTOBUF_NODISCARD PROTOBUF_NDEBUG_INLINE const char* ParseGroup(
@ -541,24 +525,6 @@ class PROTOBUF_EXPORT ParseContext : public EpsCopyInputStream {
return ptr; return ptr;
} }
template <typename TcParser, typename Table>
PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE const char* ParseGroup(
MessageLite* msg, const char* ptr, uint32_t tag, const Table* table) {
if (--depth_ < 0) return nullptr;
group_depth_++;
auto old_depth = depth_;
auto old_group_depth = group_depth_;
ptr = TcParser::ParseLoop(msg, ptr, this, table);
if (ptr != nullptr) {
ABSL_DCHECK_EQ(old_depth, depth_);
ABSL_DCHECK_EQ(old_group_depth, group_depth_);
}
group_depth_--;
depth_++;
if (PROTOBUF_PREDICT_FALSE(!ConsumeEndGroup(tag))) return nullptr;
return ptr;
}
private: private:
// Out-of-line routine to save space in ParseContext::ParseMessage<T> // Out-of-line routine to save space in ParseContext::ParseMessage<T>
// LimitToken old; // LimitToken old;
@ -1105,15 +1071,14 @@ inline int32_t ReadVarintZigZag32(const char** p) {
return WireFormatLite::ZigZagDecode32(static_cast<uint32_t>(tmp)); return WireFormatLite::ZigZagDecode32(static_cast<uint32_t>(tmp));
} }
template <typename T, typename std::enable_if< template <typename Func>
!std::is_base_of<MessageLite, T>::value, bool>::type> PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE const char*
PROTOBUF_NODISCARD const char* ParseContext::ParseMessage(T* msg, ParseContext::ParseLengthDelimitedInlined(const char* ptr, const Func& func) {
const char* ptr) {
LimitToken old; LimitToken old;
ptr = ReadSizeAndPushLimitAndDepth(ptr, &old); ptr = ReadSizeAndPushLimitAndDepthInlined(ptr, &old);
if (ptr == nullptr) return ptr; if (ptr == nullptr) return ptr;
auto old_depth = depth_; auto old_depth = depth_;
ptr = msg->_InternalParse(ptr, this); PROTOBUF_ALWAYS_INLINE_CALL ptr = func(ptr);
if (ptr != nullptr) ABSL_DCHECK_EQ(old_depth, depth_); if (ptr != nullptr) ABSL_DCHECK_EQ(old_depth, depth_);
depth_++; depth_++;
if (!PopLimit(std::move(old))) return nullptr; if (!PopLimit(std::move(old))) return nullptr;
@ -1122,13 +1087,20 @@ PROTOBUF_NODISCARD const char* ParseContext::ParseMessage(T* msg,
template <typename Func> template <typename Func>
PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE const char* PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE const char*
ParseContext::ParseLengthDelimitedInlined(const char* ptr, const Func& func) { ParseContext::ParseGroupInlined(const char* ptr, uint32_t start_tag,
LimitToken old; const Func& func) {
ptr = ReadSizeAndPushLimitAndDepthInlined(ptr, &old); if (--depth_ < 0) return nullptr;
if (ptr == nullptr) return ptr; group_depth_++;
auto old_depth = depth_;
auto old_group_depth = group_depth_;
PROTOBUF_ALWAYS_INLINE_CALL ptr = func(ptr); PROTOBUF_ALWAYS_INLINE_CALL ptr = func(ptr);
if (ptr != nullptr) {
ABSL_DCHECK_EQ(old_depth, depth_);
ABSL_DCHECK_EQ(old_group_depth, group_depth_);
}
group_depth_--;
depth_++; depth_++;
if (!PopLimit(std::move(old))) return nullptr; if (PROTOBUF_PREDICT_FALSE(!ConsumeEndGroup(start_tag))) return nullptr;
return ptr; return ptr;
} }

Loading…
Cancel
Save