15f757f3fSDimitry Andric //===--- Integral.h - Wrapper for numeric types for the VM ------*- C++ -*-===//
25f757f3fSDimitry Andric //
35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65f757f3fSDimitry Andric //
75f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
85f757f3fSDimitry Andric //
95f757f3fSDimitry Andric // Defines the VM types and helpers operating on types.
105f757f3fSDimitry Andric //
115f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
125f757f3fSDimitry Andric
135f757f3fSDimitry Andric #ifndef LLVM_CLANG_AST_INTERP_INTEGRAL_AP_H
145f757f3fSDimitry Andric #define LLVM_CLANG_AST_INTERP_INTEGRAL_AP_H
155f757f3fSDimitry Andric
165f757f3fSDimitry Andric #include "clang/AST/APValue.h"
175f757f3fSDimitry Andric #include "clang/AST/ComparisonCategories.h"
185f757f3fSDimitry Andric #include "llvm/ADT/APSInt.h"
195f757f3fSDimitry Andric #include "llvm/Support/MathExtras.h"
205f757f3fSDimitry Andric #include "llvm/Support/raw_ostream.h"
215f757f3fSDimitry Andric #include <cstddef>
225f757f3fSDimitry Andric #include <cstdint>
235f757f3fSDimitry Andric
245f757f3fSDimitry Andric #include "Primitives.h"
255f757f3fSDimitry Andric
265f757f3fSDimitry Andric namespace clang {
275f757f3fSDimitry Andric namespace interp {
285f757f3fSDimitry Andric
295f757f3fSDimitry Andric using APInt = llvm::APInt;
305f757f3fSDimitry Andric using APSInt = llvm::APSInt;
315f757f3fSDimitry Andric template <unsigned Bits, bool Signed> class Integral;
325f757f3fSDimitry Andric
335f757f3fSDimitry Andric template <bool Signed> class IntegralAP final {
345f757f3fSDimitry Andric private:
355f757f3fSDimitry Andric friend IntegralAP<!Signed>;
365f757f3fSDimitry Andric APInt V;
375f757f3fSDimitry Andric
385f757f3fSDimitry Andric template <typename T, bool InputSigned>
truncateCast(const APInt & V)395f757f3fSDimitry Andric static T truncateCast(const APInt &V) {
405f757f3fSDimitry Andric constexpr unsigned BitSize = sizeof(T) * 8;
415f757f3fSDimitry Andric if (BitSize >= V.getBitWidth()) {
425f757f3fSDimitry Andric APInt Extended;
435f757f3fSDimitry Andric if constexpr (InputSigned)
445f757f3fSDimitry Andric Extended = V.sext(BitSize);
455f757f3fSDimitry Andric else
465f757f3fSDimitry Andric Extended = V.zext(BitSize);
475f757f3fSDimitry Andric return std::is_signed_v<T> ? Extended.getSExtValue()
485f757f3fSDimitry Andric : Extended.getZExtValue();
495f757f3fSDimitry Andric }
505f757f3fSDimitry Andric
515f757f3fSDimitry Andric return std::is_signed_v<T> ? V.trunc(BitSize).getSExtValue()
525f757f3fSDimitry Andric : V.trunc(BitSize).getZExtValue();
535f757f3fSDimitry Andric }
545f757f3fSDimitry Andric
555f757f3fSDimitry Andric public:
565f757f3fSDimitry Andric using AsUnsigned = IntegralAP<false>;
575f757f3fSDimitry Andric
585f757f3fSDimitry Andric template <typename T>
IntegralAP(T Value,unsigned BitWidth)595f757f3fSDimitry Andric IntegralAP(T Value, unsigned BitWidth)
605f757f3fSDimitry Andric : V(APInt(BitWidth, static_cast<uint64_t>(Value), Signed)) {}
615f757f3fSDimitry Andric
IntegralAP(APInt V)625f757f3fSDimitry Andric IntegralAP(APInt V) : V(V) {}
635f757f3fSDimitry Andric /// Arbitrary value for uninitialized variables.
IntegralAP()64*0fca6ea1SDimitry Andric IntegralAP() : IntegralAP(-1, 3) {}
655f757f3fSDimitry Andric
665f757f3fSDimitry Andric IntegralAP operator-() const { return IntegralAP(-V); }
675f757f3fSDimitry Andric IntegralAP operator-(const IntegralAP &Other) const {
685f757f3fSDimitry Andric return IntegralAP(V - Other.V);
695f757f3fSDimitry Andric }
705f757f3fSDimitry Andric bool operator>(const IntegralAP &RHS) const {
715f757f3fSDimitry Andric if constexpr (Signed)
725f757f3fSDimitry Andric return V.ugt(RHS.V);
735f757f3fSDimitry Andric return V.sgt(RHS.V);
745f757f3fSDimitry Andric }
755f757f3fSDimitry Andric bool operator>=(IntegralAP RHS) const {
765f757f3fSDimitry Andric if constexpr (Signed)
775f757f3fSDimitry Andric return V.uge(RHS.V);
785f757f3fSDimitry Andric return V.sge(RHS.V);
795f757f3fSDimitry Andric }
805f757f3fSDimitry Andric bool operator<(IntegralAP RHS) const {
815f757f3fSDimitry Andric if constexpr (Signed)
825f757f3fSDimitry Andric return V.slt(RHS.V);
835f757f3fSDimitry Andric return V.slt(RHS.V);
845f757f3fSDimitry Andric }
855f757f3fSDimitry Andric bool operator<=(IntegralAP RHS) const {
865f757f3fSDimitry Andric if constexpr (Signed)
875f757f3fSDimitry Andric return V.ult(RHS.V);
885f757f3fSDimitry Andric return V.ult(RHS.V);
895f757f3fSDimitry Andric }
905f757f3fSDimitry Andric
915f757f3fSDimitry Andric template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>>
Ty()925f757f3fSDimitry Andric explicit operator Ty() const {
935f757f3fSDimitry Andric return truncateCast<Ty, Signed>(V);
945f757f3fSDimitry Andric }
955f757f3fSDimitry Andric
965f757f3fSDimitry Andric template <typename T> static IntegralAP from(T Value, unsigned NumBits = 0) {
975f757f3fSDimitry Andric assert(NumBits > 0);
985f757f3fSDimitry Andric APInt Copy = APInt(NumBits, static_cast<uint64_t>(Value), Signed);
995f757f3fSDimitry Andric
1005f757f3fSDimitry Andric return IntegralAP<Signed>(Copy);
1015f757f3fSDimitry Andric }
1025f757f3fSDimitry Andric
1035f757f3fSDimitry Andric template <bool InputSigned>
1045f757f3fSDimitry Andric static IntegralAP from(IntegralAP<InputSigned> V, unsigned NumBits = 0) {
1055f757f3fSDimitry Andric if (NumBits == 0)
1065f757f3fSDimitry Andric NumBits = V.bitWidth();
1075f757f3fSDimitry Andric
1085f757f3fSDimitry Andric if constexpr (InputSigned)
1095f757f3fSDimitry Andric return IntegralAP<Signed>(V.V.sextOrTrunc(NumBits));
1105f757f3fSDimitry Andric return IntegralAP<Signed>(V.V.zextOrTrunc(NumBits));
1115f757f3fSDimitry Andric }
1125f757f3fSDimitry Andric
1135f757f3fSDimitry Andric template <unsigned Bits, bool InputSigned>
from(Integral<Bits,InputSigned> I,unsigned BitWidth)1145f757f3fSDimitry Andric static IntegralAP from(Integral<Bits, InputSigned> I, unsigned BitWidth) {
1155f757f3fSDimitry Andric APInt Copy = APInt(BitWidth, static_cast<uint64_t>(I), InputSigned);
1165f757f3fSDimitry Andric
1175f757f3fSDimitry Andric return IntegralAP<Signed>(Copy);
1185f757f3fSDimitry Andric }
1195f757f3fSDimitry Andric
zero(int32_t BitWidth)1205f757f3fSDimitry Andric static IntegralAP zero(int32_t BitWidth) {
1215f757f3fSDimitry Andric APInt V = APInt(BitWidth, 0LL, Signed);
1225f757f3fSDimitry Andric return IntegralAP(V);
1235f757f3fSDimitry Andric }
1245f757f3fSDimitry Andric
bitWidth()1255f757f3fSDimitry Andric constexpr unsigned bitWidth() const { return V.getBitWidth(); }
1265f757f3fSDimitry Andric
1275f757f3fSDimitry Andric APSInt toAPSInt(unsigned Bits = 0) const {
1285f757f3fSDimitry Andric if (Bits == 0)
1295f757f3fSDimitry Andric Bits = bitWidth();
1305f757f3fSDimitry Andric
1315f757f3fSDimitry Andric if constexpr (Signed)
1325f757f3fSDimitry Andric return APSInt(V.sext(Bits), !Signed);
1335f757f3fSDimitry Andric else
1345f757f3fSDimitry Andric return APSInt(V.zext(Bits), !Signed);
1355f757f3fSDimitry Andric }
toAPValue(const ASTContext &)136*0fca6ea1SDimitry Andric APValue toAPValue(const ASTContext &) const { return APValue(toAPSInt()); }
1375f757f3fSDimitry Andric
isZero()1385f757f3fSDimitry Andric bool isZero() const { return V.isZero(); }
isPositive()1395f757f3fSDimitry Andric bool isPositive() const { return V.isNonNegative(); }
isNegative()1405f757f3fSDimitry Andric bool isNegative() const { return !V.isNonNegative(); }
isMin()1415f757f3fSDimitry Andric bool isMin() const { return V.isMinValue(); }
isMax()1425f757f3fSDimitry Andric bool isMax() const { return V.isMaxValue(); }
isSigned()1435f757f3fSDimitry Andric static constexpr bool isSigned() { return Signed; }
isMinusOne()1445f757f3fSDimitry Andric bool isMinusOne() const { return Signed && V == -1; }
1455f757f3fSDimitry Andric
countLeadingZeros()1465f757f3fSDimitry Andric unsigned countLeadingZeros() const { return V.countl_zero(); }
1475f757f3fSDimitry Andric
print(llvm::raw_ostream & OS)1485f757f3fSDimitry Andric void print(llvm::raw_ostream &OS) const { OS << V; }
toDiagnosticString(const ASTContext & Ctx)1495f757f3fSDimitry Andric std::string toDiagnosticString(const ASTContext &Ctx) const {
1505f757f3fSDimitry Andric std::string NameStr;
1515f757f3fSDimitry Andric llvm::raw_string_ostream OS(NameStr);
1525f757f3fSDimitry Andric print(OS);
1535f757f3fSDimitry Andric return NameStr;
1545f757f3fSDimitry Andric }
1555f757f3fSDimitry Andric
truncate(unsigned BitWidth)1565f757f3fSDimitry Andric IntegralAP truncate(unsigned BitWidth) const {
157*0fca6ea1SDimitry Andric if constexpr (Signed)
158*0fca6ea1SDimitry Andric return IntegralAP(V.trunc(BitWidth).sextOrTrunc(this->bitWidth()));
159*0fca6ea1SDimitry Andric else
160*0fca6ea1SDimitry Andric return IntegralAP(V.trunc(BitWidth).zextOrTrunc(this->bitWidth()));
1615f757f3fSDimitry Andric }
1625f757f3fSDimitry Andric
toUnsigned()1635f757f3fSDimitry Andric IntegralAP<false> toUnsigned() const {
1645f757f3fSDimitry Andric APInt Copy = V;
1655f757f3fSDimitry Andric return IntegralAP<false>(Copy);
1665f757f3fSDimitry Andric }
1675f757f3fSDimitry Andric
compare(const IntegralAP & RHS)1685f757f3fSDimitry Andric ComparisonCategoryResult compare(const IntegralAP &RHS) const {
1695f757f3fSDimitry Andric assert(Signed == RHS.isSigned());
1705f757f3fSDimitry Andric assert(bitWidth() == RHS.bitWidth());
1715f757f3fSDimitry Andric if constexpr (Signed) {
1725f757f3fSDimitry Andric if (V.slt(RHS.V))
1735f757f3fSDimitry Andric return ComparisonCategoryResult::Less;
1745f757f3fSDimitry Andric if (V.sgt(RHS.V))
1755f757f3fSDimitry Andric return ComparisonCategoryResult::Greater;
1765f757f3fSDimitry Andric return ComparisonCategoryResult::Equal;
1775f757f3fSDimitry Andric }
1785f757f3fSDimitry Andric
1795f757f3fSDimitry Andric assert(!Signed);
1805f757f3fSDimitry Andric if (V.ult(RHS.V))
1815f757f3fSDimitry Andric return ComparisonCategoryResult::Less;
1825f757f3fSDimitry Andric if (V.ugt(RHS.V))
1835f757f3fSDimitry Andric return ComparisonCategoryResult::Greater;
1845f757f3fSDimitry Andric return ComparisonCategoryResult::Equal;
1855f757f3fSDimitry Andric }
1865f757f3fSDimitry Andric
increment(IntegralAP A,IntegralAP * R)1875f757f3fSDimitry Andric static bool increment(IntegralAP A, IntegralAP *R) {
1885f757f3fSDimitry Andric IntegralAP<Signed> One(1, A.bitWidth());
1895f757f3fSDimitry Andric return add(A, One, A.bitWidth() + 1, R);
1905f757f3fSDimitry Andric }
1915f757f3fSDimitry Andric
decrement(IntegralAP A,IntegralAP * R)1925f757f3fSDimitry Andric static bool decrement(IntegralAP A, IntegralAP *R) {
1935f757f3fSDimitry Andric IntegralAP<Signed> One(1, A.bitWidth());
1945f757f3fSDimitry Andric return sub(A, One, A.bitWidth() + 1, R);
1955f757f3fSDimitry Andric }
1965f757f3fSDimitry Andric
add(IntegralAP A,IntegralAP B,unsigned OpBits,IntegralAP * R)1975f757f3fSDimitry Andric static bool add(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
1985f757f3fSDimitry Andric return CheckAddSubMulUB<std::plus>(A, B, OpBits, R);
1995f757f3fSDimitry Andric }
2005f757f3fSDimitry Andric
sub(IntegralAP A,IntegralAP B,unsigned OpBits,IntegralAP * R)2015f757f3fSDimitry Andric static bool sub(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
2025f757f3fSDimitry Andric return CheckAddSubMulUB<std::minus>(A, B, OpBits, R);
2035f757f3fSDimitry Andric }
2045f757f3fSDimitry Andric
mul(IntegralAP A,IntegralAP B,unsigned OpBits,IntegralAP * R)2055f757f3fSDimitry Andric static bool mul(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
2065f757f3fSDimitry Andric return CheckAddSubMulUB<std::multiplies>(A, B, OpBits, R);
2075f757f3fSDimitry Andric }
2085f757f3fSDimitry Andric
rem(IntegralAP A,IntegralAP B,unsigned OpBits,IntegralAP * R)2095f757f3fSDimitry Andric static bool rem(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
2107a6dacacSDimitry Andric if constexpr (Signed)
2117a6dacacSDimitry Andric *R = IntegralAP(A.V.srem(B.V));
2127a6dacacSDimitry Andric else
2137a6dacacSDimitry Andric *R = IntegralAP(A.V.urem(B.V));
2145f757f3fSDimitry Andric return false;
2155f757f3fSDimitry Andric }
2165f757f3fSDimitry Andric
div(IntegralAP A,IntegralAP B,unsigned OpBits,IntegralAP * R)2175f757f3fSDimitry Andric static bool div(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
2187a6dacacSDimitry Andric if constexpr (Signed)
2197a6dacacSDimitry Andric *R = IntegralAP(A.V.sdiv(B.V));
2207a6dacacSDimitry Andric else
2217a6dacacSDimitry Andric *R = IntegralAP(A.V.udiv(B.V));
2225f757f3fSDimitry Andric return false;
2235f757f3fSDimitry Andric }
2245f757f3fSDimitry Andric
bitAnd(IntegralAP A,IntegralAP B,unsigned OpBits,IntegralAP * R)2255f757f3fSDimitry Andric static bool bitAnd(IntegralAP A, IntegralAP B, unsigned OpBits,
2265f757f3fSDimitry Andric IntegralAP *R) {
2275f757f3fSDimitry Andric *R = IntegralAP(A.V & B.V);
2285f757f3fSDimitry Andric return false;
2295f757f3fSDimitry Andric }
2305f757f3fSDimitry Andric
bitOr(IntegralAP A,IntegralAP B,unsigned OpBits,IntegralAP * R)2315f757f3fSDimitry Andric static bool bitOr(IntegralAP A, IntegralAP B, unsigned OpBits,
2325f757f3fSDimitry Andric IntegralAP *R) {
2335f757f3fSDimitry Andric *R = IntegralAP(A.V | B.V);
2345f757f3fSDimitry Andric return false;
2355f757f3fSDimitry Andric }
2365f757f3fSDimitry Andric
bitXor(IntegralAP A,IntegralAP B,unsigned OpBits,IntegralAP * R)2375f757f3fSDimitry Andric static bool bitXor(IntegralAP A, IntegralAP B, unsigned OpBits,
2385f757f3fSDimitry Andric IntegralAP *R) {
2395f757f3fSDimitry Andric *R = IntegralAP(A.V ^ B.V);
2405f757f3fSDimitry Andric return false;
2415f757f3fSDimitry Andric }
2425f757f3fSDimitry Andric
neg(const IntegralAP & A,IntegralAP * R)2435f757f3fSDimitry Andric static bool neg(const IntegralAP &A, IntegralAP *R) {
2445f757f3fSDimitry Andric APInt AI = A.V;
2455f757f3fSDimitry Andric AI.negate();
2465f757f3fSDimitry Andric *R = IntegralAP(AI);
2475f757f3fSDimitry Andric return false;
2485f757f3fSDimitry Andric }
2495f757f3fSDimitry Andric
comp(IntegralAP A,IntegralAP * R)2505f757f3fSDimitry Andric static bool comp(IntegralAP A, IntegralAP *R) {
2515f757f3fSDimitry Andric *R = IntegralAP(~A.V);
2525f757f3fSDimitry Andric return false;
2535f757f3fSDimitry Andric }
2545f757f3fSDimitry Andric
shiftLeft(const IntegralAP A,const IntegralAP B,unsigned OpBits,IntegralAP * R)2555f757f3fSDimitry Andric static void shiftLeft(const IntegralAP A, const IntegralAP B, unsigned OpBits,
2565f757f3fSDimitry Andric IntegralAP *R) {
2575f757f3fSDimitry Andric *R = IntegralAP(A.V.shl(B.V.getZExtValue()));
2585f757f3fSDimitry Andric }
2595f757f3fSDimitry Andric
shiftRight(const IntegralAP A,const IntegralAP B,unsigned OpBits,IntegralAP * R)2605f757f3fSDimitry Andric static void shiftRight(const IntegralAP A, const IntegralAP B,
2615f757f3fSDimitry Andric unsigned OpBits, IntegralAP *R) {
2625f757f3fSDimitry Andric unsigned ShiftAmount = B.V.getZExtValue();
2635f757f3fSDimitry Andric if constexpr (Signed)
2645f757f3fSDimitry Andric *R = IntegralAP(A.V.ashr(ShiftAmount));
2655f757f3fSDimitry Andric else
2665f757f3fSDimitry Andric *R = IntegralAP(A.V.lshr(ShiftAmount));
2675f757f3fSDimitry Andric }
2685f757f3fSDimitry Andric
269*0fca6ea1SDimitry Andric // === Serialization support ===
bytesToSerialize()270*0fca6ea1SDimitry Andric size_t bytesToSerialize() const {
271*0fca6ea1SDimitry Andric // 4 bytes for the BitWidth followed by N bytes for the actual APInt.
272*0fca6ea1SDimitry Andric return sizeof(uint32_t) + (V.getBitWidth() / CHAR_BIT);
273*0fca6ea1SDimitry Andric }
274*0fca6ea1SDimitry Andric
serialize(std::byte * Buff)275*0fca6ea1SDimitry Andric void serialize(std::byte *Buff) const {
276*0fca6ea1SDimitry Andric assert(V.getBitWidth() < std::numeric_limits<uint8_t>::max());
277*0fca6ea1SDimitry Andric uint32_t BitWidth = V.getBitWidth();
278*0fca6ea1SDimitry Andric
279*0fca6ea1SDimitry Andric std::memcpy(Buff, &BitWidth, sizeof(uint32_t));
280*0fca6ea1SDimitry Andric llvm::StoreIntToMemory(V, (uint8_t *)(Buff + sizeof(uint32_t)),
281*0fca6ea1SDimitry Andric BitWidth / CHAR_BIT);
282*0fca6ea1SDimitry Andric }
283*0fca6ea1SDimitry Andric
deserialize(const std::byte * Buff)284*0fca6ea1SDimitry Andric static IntegralAP<Signed> deserialize(const std::byte *Buff) {
285*0fca6ea1SDimitry Andric uint32_t BitWidth;
286*0fca6ea1SDimitry Andric std::memcpy(&BitWidth, Buff, sizeof(uint32_t));
287*0fca6ea1SDimitry Andric IntegralAP<Signed> Val(APInt(BitWidth, 0ull, !Signed));
288*0fca6ea1SDimitry Andric
289*0fca6ea1SDimitry Andric llvm::LoadIntFromMemory(Val.V, (const uint8_t *)Buff + sizeof(uint32_t),
290*0fca6ea1SDimitry Andric BitWidth / CHAR_BIT);
291*0fca6ea1SDimitry Andric return Val;
292*0fca6ea1SDimitry Andric }
293*0fca6ea1SDimitry Andric
2945f757f3fSDimitry Andric private:
2955f757f3fSDimitry Andric template <template <typename T> class Op>
CheckAddSubMulUB(const IntegralAP & A,const IntegralAP & B,unsigned BitWidth,IntegralAP * R)2965f757f3fSDimitry Andric static bool CheckAddSubMulUB(const IntegralAP &A, const IntegralAP &B,
2975f757f3fSDimitry Andric unsigned BitWidth, IntegralAP *R) {
2985f757f3fSDimitry Andric if constexpr (!Signed) {
2995f757f3fSDimitry Andric R->V = Op<APInt>{}(A.V, B.V);
3005f757f3fSDimitry Andric return false;
3015f757f3fSDimitry Andric }
3025f757f3fSDimitry Andric
3035f757f3fSDimitry Andric const APSInt &LHS = A.toAPSInt();
3045f757f3fSDimitry Andric const APSInt &RHS = B.toAPSInt();
3055f757f3fSDimitry Andric APSInt Value = Op<APSInt>{}(LHS.extend(BitWidth), RHS.extend(BitWidth));
3065f757f3fSDimitry Andric APSInt Result = Value.trunc(LHS.getBitWidth());
3075f757f3fSDimitry Andric R->V = Result;
3085f757f3fSDimitry Andric
3095f757f3fSDimitry Andric return Result.extend(BitWidth) != Value;
3105f757f3fSDimitry Andric }
3115f757f3fSDimitry Andric };
3125f757f3fSDimitry Andric
3135f757f3fSDimitry Andric template <bool Signed>
3145f757f3fSDimitry Andric inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
3155f757f3fSDimitry Andric IntegralAP<Signed> I) {
3165f757f3fSDimitry Andric I.print(OS);
3175f757f3fSDimitry Andric return OS;
3185f757f3fSDimitry Andric }
3195f757f3fSDimitry Andric
320*0fca6ea1SDimitry Andric template <bool Signed>
getSwappedBytes(IntegralAP<Signed> F)321*0fca6ea1SDimitry Andric IntegralAP<Signed> getSwappedBytes(IntegralAP<Signed> F) {
322*0fca6ea1SDimitry Andric return F;
323*0fca6ea1SDimitry Andric }
324*0fca6ea1SDimitry Andric
3255f757f3fSDimitry Andric } // namespace interp
3265f757f3fSDimitry Andric } // namespace clang
3275f757f3fSDimitry Andric
3285f757f3fSDimitry Andric #endif
329