From 411cba1314e4219a16185093448471c14c22fc6f Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Tue, 1 Nov 2022 15:03:32 -0700 Subject: [PATCH] Call ParseVarint with a pointer to the specific result instead of always passing in a uint64_t * when possible. Specialize ParseVarint for bool, which then takes up much less code. PiperOrigin-RevId: 485427698 --- .../generated_message_tctable_lite.cc | 84 +++++++++++++++++-- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/src/google/protobuf/generated_message_tctable_lite.cc b/src/google/protobuf/generated_message_tctable_lite.cc index cb4f2cbab7..e146aefe42 100644 --- a/src/google/protobuf/generated_message_tctable_lite.cc +++ b/src/google/protobuf/generated_message_tctable_lite.cc @@ -789,31 +789,99 @@ done10: return {p + 10, res1 & res2 & res3}; } +template inline PROTOBUF_ALWAYS_INLINE const char* ParseVarint(const char* p, - uint64_t* value) { + Type* value) { + static_assert(sizeof(Type) == 4 || sizeof(Type) == 8, + "Only [u]int32_t and [u]int64_t please"); int64_t byte = static_cast(*p); if (PROTOBUF_PREDICT_TRUE(byte >= 0)) { *value = byte; return p + 1; } else { auto tmp = Parse64FallbackPair(p, byte); - if (PROTOBUF_PREDICT_TRUE(tmp.first)) *value = tmp.second; + if (PROTOBUF_PREDICT_TRUE(tmp.first)) { + *value = static_cast(tmp.second); + } return tmp.first; } } +// This overload is specifically for handling bool, because bools have very +// different requirements and performance opportunities than ints. +inline PROTOBUF_ALWAYS_INLINE const char* ParseVarint(const char* p, + bool* value) { + unsigned char byte = static_cast(*p++); + if (PROTOBUF_PREDICT_TRUE(byte == 0 || byte == 1)) { + // This is the code path almost always taken, + // so we take care to make it very efficient. + if (sizeof(byte) == sizeof(*value)) { + memcpy(value, &byte, 1); + } else { + // The C++ standard does not specify that a `bool` takes only one byte + *value = byte; + } + return p; + } + // This part, we just care about code size. + // Although it's almost never used, we have to support it because we guarantee + // compatibility for users who change a field from an int32 or int64 to a bool + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + byte = (byte - 0x80) | *p++; + if (PROTOBUF_PREDICT_FALSE(byte & 0x80)) { + return nullptr; + } + } + } + } + } + } + } + } + } + } + *value = byte; + return p; +} + template -inline FieldType ZigZagDecodeHelper(uint64_t value) { +inline FieldType ZigZagDecodeHelper(FieldType value) { return static_cast(value); } template <> -inline int32_t ZigZagDecodeHelper(uint64_t value) { +inline uint32_t ZigZagDecodeHelper(uint32_t value) { return WireFormatLite::ZigZagDecode32(value); } template <> -inline int64_t ZigZagDecodeHelper(uint64_t value) { +inline int32_t ZigZagDecodeHelper(int32_t value) { + return WireFormatLite::ZigZagDecode32(value); +} + +template <> +inline uint64_t ZigZagDecodeHelper(uint64_t value) { + return WireFormatLite::ZigZagDecode64(value); +} + +template <> +inline int64_t ZigZagDecodeHelper(int64_t value) { return WireFormatLite::ZigZagDecode64(value); } @@ -868,11 +936,11 @@ PROTOBUF_NOINLINE const char* TcParser::SingularVarBigint( #if defined(__GNUC__) // This empty asm block convinces the compiler that the contents of spill may // have changed, and thus can't be cached in registers. It's similar to, but - // more optimal then, the effect of declaring it "volatile". + // more optimal than, the effect of declaring it "volatile". asm("" : "+m"(spill)); #endif - uint64_t tmp; + FieldType tmp; PROTOBUF_ASSUME(static_cast(*ptr) < 0); ptr = ParseVarint(ptr, &tmp); @@ -946,7 +1014,7 @@ PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedVarint( const auto expected_tag = UnalignedLoad(ptr); do { ptr += sizeof(TagType); - uint64_t tmp; + FieldType tmp; ptr = ParseVarint(ptr, &tmp); if (ptr == nullptr) { return Error(PROTOBUF_TC_PARAM_PASS);