From: Davide Italiano Date: Mon, 2 Apr 2018 16:50:54 +0000 (+0000) Subject: [Core] Grab-bag of improvements for Scalar. X-Git-Tag: llvmorg-7.0.0-rc1~9205 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=49d802862d5edf762928cb2ee159614f31eb2364;p=platform%2Fupstream%2Fllvm.git [Core] Grab-bag of improvements for Scalar. Remove Scalar::Cast. It was noted on the list that this method is unused. So, this patch removes it. Fix Scalar::Promote for most integer types This fixes promotion of most integer types (128- and 256-bit types are handled in a subsequent patch) to floating-point types. Previously promotion was done bitwise, where value preservation is correct. Fix Scalar::Promote for 128- and 256-bit integer types This patch fixes the behavior of Scalar::Promote when trying to perform a binary operation involving a 128- or 256-bit integer type and a floating-point type. Now, the integer is cast to the floating point type for the operation. Patch by Tom Tromey! Differential Revision: https://reviews.llvm.org/D44907 llvm-svn: 328985 --- diff --git a/lldb/include/lldb/Core/Scalar.h b/lldb/include/lldb/Core/Scalar.h index 943398b..07425f9 100644 --- a/lldb/include/lldb/Core/Scalar.h +++ b/lldb/include/lldb/Core/Scalar.h @@ -50,13 +50,13 @@ public: e_ulong, e_slonglong, e_ulonglong, - e_float, - e_double, - e_long_double, - e_uint128, e_sint128, + e_uint128, + e_sint256, e_uint256, - e_sint256 + e_float, + e_double, + e_long_double }; //------------------------------------------------------------------ @@ -165,8 +165,6 @@ public: bool Promote(Scalar::Type type); - bool Cast(Scalar::Type type); - bool MakeSigned(); bool MakeUnsigned(); diff --git a/lldb/source/Core/Scalar.cpp b/lldb/source/Core/Scalar.cpp index 4ef29e7..451981a 100644 --- a/lldb/source/Core/Scalar.cpp +++ b/lldb/source/Core/Scalar.cpp @@ -491,20 +491,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -551,20 +555,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -607,20 +615,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -659,20 +671,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -707,20 +723,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -751,20 +771,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -795,20 +819,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -835,20 +863,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -875,20 +907,24 @@ bool Scalar::Promote(Scalar::Type type) { break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, true, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -911,20 +947,24 @@ bool Scalar::Promote(Scalar::Type type) { success = true; break; case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); + m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); + m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); + m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended()); + m_float.convertFromAPInt(m_integer, false, + llvm::APFloat::rmNearestTiesToEven); success = true; break; } @@ -948,20 +988,19 @@ bool Scalar::Promote(Scalar::Type type) { success = true; break; case e_double: - m_float = llvm::APFloat((float_t)m_float.convertToFloat()); + m_float = llvm::APFloat((double_t)m_float.convertToFloat()); success = true; break; - case e_long_double: - if (m_ieee_quad) - m_float = - llvm::APFloat(llvm::APFloat::IEEEquad(), m_float.bitcastToAPInt()); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), - m_float.bitcastToAPInt()); + case e_long_double: { + bool ignore; + m_float.convert(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended(), + llvm::APFloat::rmNearestTiesToEven, &ignore); success = true; break; } + } break; case e_double: @@ -982,16 +1021,15 @@ bool Scalar::Promote(Scalar::Type type) { case e_double: success = true; break; - case e_long_double: - if (m_ieee_quad) - m_float = - llvm::APFloat(llvm::APFloat::IEEEquad(), m_float.bitcastToAPInt()); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), - m_float.bitcastToAPInt()); + case e_long_double: { + bool ignore; + m_float.convert(m_ieee_quad ? llvm::APFloat::IEEEquad() + : llvm::APFloat::x87DoubleExtended(), + llvm::APFloat::rmNearestTiesToEven, &ignore); success = true; break; } + } break; case e_long_double: @@ -1088,253 +1126,6 @@ Scalar::Type Scalar::GetValueTypeForFloatWithByteSize(size_t byte_size) { return e_void; } -bool Scalar::Cast(Scalar::Type type) { - bool success = false; - switch (m_type) { - case e_void: - break; - - case e_sint: - case e_uint: - case e_slong: - case e_ulong: - case e_slonglong: - case e_ulonglong: - case e_sint128: - case e_uint128: - case e_sint256: - case e_uint256: - switch (type) { - case e_void: - break; - case e_sint: - m_integer = m_integer.sextOrTrunc(sizeof(sint_t) * 8); - success = true; - break; - - case e_uint: - m_integer = m_integer.zextOrTrunc(sizeof(sint_t) * 8); - success = true; - break; - - case e_slong: - m_integer = m_integer.sextOrTrunc(sizeof(slong_t) * 8); - success = true; - break; - - case e_ulong: - m_integer = m_integer.zextOrTrunc(sizeof(slong_t) * 8); - success = true; - break; - - case e_slonglong: - m_integer = m_integer.sextOrTrunc(sizeof(slonglong_t) * 8); - success = true; - break; - - case e_ulonglong: - m_integer = m_integer.zextOrTrunc(sizeof(slonglong_t) * 8); - success = true; - break; - - case e_sint128: - m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128); - success = true; - break; - - case e_uint128: - m_integer = m_integer.zextOrTrunc(BITWIDTH_INT128); - success = true; - break; - - case e_sint256: - m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256); - success = true; - break; - - case e_uint256: - m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256); - success = true; - break; - - case e_float: - m_float = llvm::APFloat(m_integer.bitsToFloat()); - success = true; - break; - - case e_double: - m_float = llvm::APFloat(m_integer.bitsToDouble()); - success = true; - break; - - case e_long_double: - if (m_ieee_quad) - m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), m_integer); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), m_integer); - success = true; - break; - } - break; - - case e_float: - switch (type) { - case e_void: - break; - case e_sint: - case e_uint: - case e_slong: - case e_ulong: - case e_slonglong: - case e_ulonglong: - case e_sint128: - case e_uint128: - case e_sint256: - case e_uint256: - m_integer = m_float.bitcastToAPInt(); - success = true; - break; - case e_float: - m_float = llvm::APFloat(m_float.convertToFloat()); - success = true; - break; - case e_double: - m_float = llvm::APFloat(m_float.convertToFloat()); - success = true; - break; - case e_long_double: - if (m_ieee_quad) - m_float = - llvm::APFloat(llvm::APFloat::IEEEquad(), m_float.bitcastToAPInt()); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), - m_float.bitcastToAPInt()); - success = true; - break; - } - break; - - case e_double: - switch (type) { - case e_void: - break; - case e_sint: - case e_uint: - case e_slong: - case e_ulong: - case e_slonglong: - case e_ulonglong: - case e_sint128: - case e_uint128: - case e_sint256: - case e_uint256: - m_integer = m_float.bitcastToAPInt(); - success = true; - break; - case e_float: - m_float = llvm::APFloat(m_float.convertToDouble()); - success = true; - break; - case e_double: - m_float = llvm::APFloat(m_float.convertToDouble()); - success = true; - break; - case e_long_double: - if (m_ieee_quad) - m_float = - llvm::APFloat(llvm::APFloat::IEEEquad(), m_float.bitcastToAPInt()); - else - m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), - m_float.bitcastToAPInt()); - success = true; - break; - } - break; - - case e_long_double: - switch (type) { - case e_void: - break; - case e_sint: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.sextOrTrunc(sizeof(sint_t) * 8); - success = true; - break; - - case e_uint: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.zextOrTrunc(sizeof(sint_t) * 8); - success = true; - break; - - case e_slong: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.sextOrTrunc(sizeof(slong_t) * 8); - success = true; - break; - - case e_ulong: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.zextOrTrunc(sizeof(slong_t) * 8); - success = true; - break; - - case e_slonglong: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.sextOrTrunc(sizeof(slonglong_t) * 8); - success = true; - break; - - case e_ulonglong: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.zextOrTrunc(sizeof(slonglong_t) * 8); - success = true; - break; - - case e_sint128: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128); - success = true; - break; - - case e_uint128: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.zextOrTrunc(BITWIDTH_INT128); - success = true; - break; - - case e_sint256: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256); - success = true; - break; - - case e_uint256: - m_integer = m_float.bitcastToAPInt(); - m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256); - success = true; - break; - - case e_float: - m_float = llvm::APFloat(m_float.convertToFloat()); - success = true; - break; - case e_double: - m_float = llvm::APFloat(m_float.convertToFloat()); - success = true; - break; - case e_long_double: - success = true; - break; - } - break; - } - - if (success) - m_type = type; - return success; -} - bool Scalar::MakeSigned() { bool success = false; @@ -1819,7 +1610,7 @@ float Scalar::Float(float fail_value) const { case e_uint128: case e_sint256: case e_uint256: - return m_integer.bitsToFloat(); + return llvm::APIntOps::RoundAPIntToFloat(m_integer); case e_float: return m_float.convertToFloat(); case e_double: @@ -1845,7 +1636,7 @@ double Scalar::Double(double fail_value) const { case e_uint128: case e_sint256: case e_uint256: - return m_integer.bitsToDouble(); + return llvm::APIntOps::RoundAPIntToDouble(m_integer); case e_float: return (double_t)m_float.convertToFloat(); case e_double: @@ -1871,7 +1662,7 @@ long double Scalar::LongDouble(long double fail_value) const { case e_uint128: case e_sint256: case e_uint256: - return (long_double_t)m_integer.bitsToDouble(); + return (long_double_t)llvm::APIntOps::RoundAPIntToDouble(m_integer); case e_float: return (long_double_t)m_float.convertToFloat(); case e_double: diff --git a/lldb/unittests/Core/ScalarTest.cpp b/lldb/unittests/Core/ScalarTest.cpp index 47fa021..6ccc390 100644 --- a/lldb/unittests/Core/ScalarTest.cpp +++ b/lldb/unittests/Core/ScalarTest.cpp @@ -73,6 +73,12 @@ TEST(ScalarTest, CastOperations) { ASSERT_EQ((unsigned long)a, a_scalar.ULong()); ASSERT_EQ((signed long long)a, a_scalar.SLongLong()); ASSERT_EQ((unsigned long long)a, a_scalar.ULongLong()); + + int a2 = 23; + Scalar a2_scalar(a2); + ASSERT_EQ((float)a2, a2_scalar.Float()); + ASSERT_EQ((double)a2, a2_scalar.Double()); + ASSERT_EQ((long double)a2, a2_scalar.LongDouble()); } TEST(ScalarTest, ExtractBitfield) { @@ -140,3 +146,42 @@ TEST(ScalarTest, Division) { EXPECT_TRUE(r.IsValid()); EXPECT_EQ(r, Scalar(2.5)); } + +TEST(ScalarTest, Promotion) { + static Scalar::Type int_types[] = { + Scalar::e_sint, Scalar::e_uint, Scalar::e_slong, + Scalar::e_ulong, Scalar::e_slonglong, Scalar::e_ulonglong, + Scalar::e_sint128, Scalar::e_uint128, Scalar::e_sint256, + Scalar::e_uint256, + Scalar::e_void // sentinel + }; + + static Scalar::Type float_types[] = { + Scalar::e_float, Scalar::e_double, Scalar::e_long_double, + Scalar::e_void // sentinel + }; + + for (int i = 0; int_types[i] != Scalar::e_void; ++i) { + for (int j = 0; float_types[j] != Scalar::e_void; ++j) { + Scalar lhs(2); + EXPECT_TRUE(lhs.Promote(int_types[i])) << "int promotion #" << i; + Scalar rhs(0.5f); + EXPECT_TRUE(rhs.Promote(float_types[j])) << "float promotion #" << j; + Scalar x(2.5f); + EXPECT_TRUE(x.Promote(float_types[j])); + EXPECT_EQ(lhs + rhs, x); + } + } + + for (int i = 0; float_types[i] != Scalar::e_void; ++i) { + for (int j = 0; float_types[j] != Scalar::e_void; ++j) { + Scalar lhs(2); + EXPECT_TRUE(lhs.Promote(float_types[i])) << "float promotion #" << i; + Scalar rhs(0.5f); + EXPECT_TRUE(rhs.Promote(float_types[j])) << "float promotion #" << j; + Scalar x(2.5f); + EXPECT_TRUE(x.Promote(float_types[j])); + EXPECT_EQ(lhs + rhs, x); + } + } +}