| //===- Value.cpp - Value Representation for llubi -------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements utility functions for the value representation. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Value.h" |
| #include "Context.h" |
| #include "llvm/ADT/SmallString.h" |
| |
| namespace llvm::ubi { |
| |
| IntrusiveRefCntPtr<Provenance> Provenance::nullary() { |
| static IntrusiveRefCntPtr<Provenance> Instance = |
| makeIntrusiveRefCnt<Provenance>(nullptr); |
| return Instance; |
| } |
| |
| IntrusiveRefCntPtr<Provenance> |
| Provenance::getWithKnownMemoryObject(MemoryObject &KnownObj) { |
| assert(!Obj && Wildcard && "The memory object has been determined."); |
| auto Res = makeIntrusiveRefCnt<Provenance>(*this); |
| Res->Obj = &KnownObj; |
| Res->Tag = APInt(); |
| return Res; |
| } |
| |
| void Pointer::print(raw_ostream &OS) const { |
| SmallString<32> AddrStr; |
| Address.toStringUnsigned(AddrStr, 16); |
| OS << "ptr 0x" << AddrStr << " ["; |
| if (MemoryObject *Obj = Prov->getMemoryObject()) { |
| if (Obj->isIRGlobalValue()) |
| OS << "@"; |
| OS << Obj->getName(); |
| if (Address != Obj->getAddress()) |
| OS << " + " << (Address - Obj->getAddress()); |
| MemoryObjectState State = Obj->getState(); |
| if (State != MemoryObjectState::Alive) |
| OS << (State == MemoryObjectState::Dead ? " (dead)" : " (dangling)"); |
| } else { |
| OS << (Prov->isWildcard() ? "wildcard" : "nullary"); |
| } |
| // TODO: print provenance |
| OS << "]"; |
| } |
| |
| AnyValue Pointer::null(unsigned AS, const DataLayout &DL) { |
| return AnyValue(Pointer(Provenance::nullary(), DL.getNullPtrValue(AS))); |
| } |
| |
| bool Pointer::isNullPtr(unsigned AS, const DataLayout &DL) const { |
| return Address == DL.getNullPtrValue(AS); |
| } |
| |
| void AnyValue::print(Context &Ctx, raw_ostream &OS) const { |
| switch (Kind) { |
| case StorageKind::Integer: |
| if (IntVal.getBitWidth() == 1) { |
| OS << (IntVal.getBoolValue() ? "T" : "F"); |
| break; |
| } |
| OS << "i" << IntVal.getBitWidth() << ' ' << IntVal; |
| break; |
| case StorageKind::Float: { |
| switch (APFloat::SemanticsToEnum(FloatVal.getSemantics())) { |
| default: |
| llvm_unreachable("invalid fltSemantics"); |
| case APFloatBase::S_IEEEhalf: |
| OS << "half "; |
| break; |
| case APFloatBase::S_BFloat: |
| OS << "bfloat "; |
| break; |
| case APFloatBase::S_IEEEsingle: |
| OS << "float "; |
| break; |
| case APFloatBase::S_IEEEdouble: |
| OS << "double "; |
| break; |
| case APFloatBase::S_x87DoubleExtended: |
| OS << "x86_fp80 "; |
| break; |
| case APFloatBase::S_IEEEquad: |
| OS << "fp128 "; |
| break; |
| case APFloatBase::S_PPCDoubleDouble: |
| OS << "ppc_fp128 "; |
| break; |
| } |
| // We cannot reuse Value::print due to lack of LLVMContext here. |
| // Similar to writeAPFloatInternal, output the FP constant value in |
| // exponential notation if it is lossless, otherwise output it in |
| // hexadecimal notation. |
| SmallString<16> StrVal; |
| FloatVal.toString(StrVal, /*FormatPrecision=*/6, /*FormatMaxPadding=*/0, |
| /*TruncateZero=*/false); |
| if (APFloat(FloatVal.getSemantics(), StrVal).bitwiseIsEqual(FloatVal)) { |
| OS << StrVal; |
| } else { |
| StrVal.clear(); |
| APInt Bits = FloatVal.bitcastToAPInt(); |
| Bits.toStringUnsigned(StrVal, 16); |
| size_t MaxDigits = divideCeil(Bits.getBitWidth(), 4); |
| OS << "0x"; |
| for (size_t Digits = StrVal.size(); Digits != MaxDigits; ++Digits) |
| OS << '0'; |
| OS << StrVal; |
| } |
| break; |
| } |
| case StorageKind::Pointer: |
| PtrVal.print(OS); |
| break; |
| case StorageKind::Byte: |
| ByteVal.print(Ctx, OS); |
| break; |
| case StorageKind::Poison: |
| OS << "poison"; |
| break; |
| case StorageKind::None: |
| OS << "none"; |
| break; |
| case StorageKind::Aggregate: |
| OS << "{ "; |
| for (size_t I = 0, E = AggVal.size(); I != E; ++I) { |
| if (I != 0) |
| OS << ", "; |
| AggVal[I].print(Ctx, OS); |
| } |
| OS << " }"; |
| break; |
| } |
| } |
| |
| void AnyValue::destroy() { |
| switch (Kind) { |
| case StorageKind::Integer: |
| IntVal.~APInt(); |
| break; |
| case StorageKind::Float: |
| FloatVal.~APFloat(); |
| break; |
| case StorageKind::Pointer: |
| PtrVal.~Pointer(); |
| break; |
| case StorageKind::Byte: |
| ByteVal.~ByteValue(); |
| break; |
| case StorageKind::Poison: |
| case StorageKind::None: |
| break; |
| case StorageKind::Aggregate: |
| AggVal.~vector(); |
| break; |
| } |
| } |
| |
| AnyValue::AnyValue(const AnyValue &Other) : Kind(Other.Kind) { |
| switch (Other.Kind) { |
| case StorageKind::Integer: |
| new (&IntVal) APInt(Other.IntVal); |
| break; |
| case StorageKind::Float: |
| new (&FloatVal) APFloat(Other.FloatVal); |
| break; |
| case StorageKind::Pointer: |
| new (&PtrVal) Pointer(Other.PtrVal); |
| break; |
| case StorageKind::Byte: |
| new (&ByteVal) ByteValue(Other.ByteVal); |
| break; |
| case StorageKind::Poison: |
| case StorageKind::None: |
| break; |
| case StorageKind::Aggregate: |
| new (&AggVal) std::vector<AnyValue>(Other.AggVal); |
| break; |
| } |
| } |
| AnyValue::AnyValue(AnyValue &&Other) : Kind(Other.Kind) { |
| switch (Other.Kind) { |
| case StorageKind::Integer: |
| new (&IntVal) APInt(std::move(Other.IntVal)); |
| break; |
| case StorageKind::Float: |
| new (&FloatVal) APFloat(std::move(Other.FloatVal)); |
| break; |
| case StorageKind::Pointer: |
| new (&PtrVal) Pointer(std::move(Other.PtrVal)); |
| break; |
| case StorageKind::Byte: |
| new (&ByteVal) ByteValue(std::move(Other.ByteVal)); |
| break; |
| case StorageKind::Poison: |
| case StorageKind::None: |
| break; |
| case StorageKind::Aggregate: |
| new (&AggVal) std::vector<AnyValue>(std::move(Other.AggVal)); |
| break; |
| } |
| } |
| |
| AnyValue &AnyValue::operator=(const AnyValue &Other) { |
| if (&Other == this) |
| return *this; |
| |
| destroy(); |
| Kind = Other.Kind; |
| switch (Other.Kind) { |
| case StorageKind::Integer: |
| new (&IntVal) APInt(Other.IntVal); |
| break; |
| case StorageKind::Float: |
| new (&FloatVal) APFloat(Other.FloatVal); |
| break; |
| case StorageKind::Pointer: |
| new (&PtrVal) Pointer(Other.PtrVal); |
| break; |
| case StorageKind::Byte: |
| new (&ByteVal) ByteValue(Other.ByteVal); |
| break; |
| case StorageKind::Poison: |
| case StorageKind::None: |
| break; |
| case StorageKind::Aggregate: |
| new (&AggVal) std::vector<AnyValue>(Other.AggVal); |
| break; |
| } |
| |
| return *this; |
| } |
| AnyValue &AnyValue::operator=(AnyValue &&Other) { |
| if (&Other == this) |
| return *this; |
| destroy(); |
| Kind = Other.Kind; |
| switch (Other.Kind) { |
| case StorageKind::Integer: |
| new (&IntVal) APInt(std::move(Other.IntVal)); |
| break; |
| case StorageKind::Float: |
| new (&FloatVal) APFloat(std::move(Other.FloatVal)); |
| break; |
| case StorageKind::Pointer: |
| new (&PtrVal) Pointer(std::move(Other.PtrVal)); |
| break; |
| case StorageKind::Byte: |
| new (&ByteVal) ByteValue(std::move(Other.ByteVal)); |
| break; |
| case StorageKind::Poison: |
| case StorageKind::None: |
| break; |
| case StorageKind::Aggregate: |
| new (&AggVal) std::vector<AnyValue>(std::move(Other.AggVal)); |
| break; |
| } |
| |
| return *this; |
| } |
| |
| AnyValue AnyValue::getPoisonValue(Context &Ctx, Type *Ty) { |
| if (Ty->isFloatingPointTy() || Ty->isIntegerTy() || Ty->isPointerTy()) |
| return AnyValue::poison(); |
| if (Ty->isByteTy()) |
| return ByteValue::poison(Ty->getByteBitWidth(), |
| Ctx.getDataLayout().isLittleEndian()); |
| if (auto *VecTy = dyn_cast<VectorType>(Ty)) { |
| uint32_t NumElements = Ctx.getEVL(VecTy->getElementCount()); |
| return AnyValue(std::vector<AnyValue>( |
| NumElements, getPoisonValue(Ctx, VecTy->getScalarType()))); |
| } |
| if (auto *ArrTy = dyn_cast<ArrayType>(Ty)) { |
| uint64_t NumElements = ArrTy->getNumElements(); |
| return AnyValue(std::vector<AnyValue>( |
| NumElements, getPoisonValue(Ctx, ArrTy->getElementType()))); |
| } |
| if (auto *StructTy = dyn_cast<StructType>(Ty)) { |
| std::vector<AnyValue> Elements; |
| Elements.reserve(StructTy->getNumElements()); |
| for (uint32_t I = 0, E = StructTy->getNumElements(); I != E; ++I) |
| Elements.push_back(getPoisonValue(Ctx, StructTy->getElementType(I))); |
| return AnyValue(std::move(Elements)); |
| } |
| llvm_unreachable("Unsupported type"); |
| } |
| AnyValue AnyValue::getNullValue(Context &Ctx, Type *Ty) { |
| if (Ty->isIntegerTy()) |
| return AnyValue(APInt::getZero(Ty->getIntegerBitWidth())); |
| if (Ty->isFloatingPointTy()) |
| return AnyValue(APFloat::getZero(Ty->getFltSemantics())); |
| if (Ty->isPointerTy()) |
| return Pointer::null(Ty->getPointerAddressSpace(), Ctx.getDataLayout()); |
| if (Ty->isByteTy()) |
| return ByteValue::zero(Ty->getByteBitWidth(), |
| Ctx.getDataLayout().isLittleEndian()); |
| if (auto *VecTy = dyn_cast<VectorType>(Ty)) { |
| uint32_t NumElements = Ctx.getEVL(VecTy->getElementCount()); |
| return AnyValue(std::vector<AnyValue>( |
| NumElements, getNullValue(Ctx, VecTy->getElementType()))); |
| } |
| if (auto *ArrTy = dyn_cast<ArrayType>(Ty)) { |
| uint64_t NumElements = ArrTy->getNumElements(); |
| return AnyValue(std::vector<AnyValue>( |
| NumElements, getNullValue(Ctx, ArrTy->getElementType()))); |
| } |
| if (auto *StructTy = dyn_cast<StructType>(Ty)) { |
| std::vector<AnyValue> Elements; |
| Elements.reserve(StructTy->getNumElements()); |
| for (uint32_t I = 0, E = StructTy->getNumElements(); I != E; ++I) |
| Elements.push_back(getNullValue(Ctx, StructTy->getElementType(I))); |
| return AnyValue(std::move(Elements)); |
| } |
| llvm_unreachable("Unsupported type"); |
| } |
| |
| AnyValue AnyValue::getVectorSplat(const AnyValue &Scalar, size_t NumElements) { |
| assert(!Scalar.isAggregate() && !Scalar.isNone() && "Expect a scalar value"); |
| return AnyValue(std::vector<AnyValue>(NumElements, Scalar)); |
| } |
| |
| ByteValue::ByteValue(const APInt &V, bool IsLittleEndian) |
| : BitWidth(V.getBitWidth()), IsLittleEndian(IsLittleEndian) { |
| Val.resize(divideCeil(BitWidth, 8)); |
| MutableBytesView View(Val, IsLittleEndian); |
| for (uint32_t I = 0; I < BitWidth; I += 8) |
| View[I / 8] = Byte::concrete(static_cast<uint8_t>( |
| V.extractBitsAsZExtValue(std::min(BitWidth - I, 8U), I))); |
| } |
| ByteValue ByteValue::zero(uint32_t BitWidth, bool IsLittleEndian) { |
| return ByteValue( |
| BitWidth, std::vector<Byte>(divideCeil(BitWidth, 8), Byte::concrete(0)), |
| IsLittleEndian); |
| } |
| |
| ByteValue ByteValue::poison(uint32_t BitWidth, bool IsLittleEndian) { |
| return ByteValue(BitWidth, |
| std::vector<Byte>(divideCeil(BitWidth, 8), Byte::poison()), |
| IsLittleEndian, /*ImplicitClearHighBits=*/true); |
| } |
| |
| void ByteValue::print(Context &Ctx, raw_ostream &OS) const { |
| OS << 'b' << BitWidth << ' '; |
| |
| auto PrintByte = [&](const Byte &V) { |
| bool IsFullByte = (BitWidth & 7) == 0 || |
| (IsLittleEndian ? &Val.back() : &Val.front()) != &V; |
| // Try to print a byte in short form |
| if (IsFullByte && V.ConcreteMask == 255 && V.TagMask == 0) { |
| // Concrete value without provenance. |
| OS << "0x" << hexdigit(V.Value >> 4) << hexdigit(V.Value & 15); |
| } else if (IsFullByte && V.ConcreteMask == 0) { |
| assert(V.Value == 0 && "Byte values don't contain undef bits."); |
| // Poison bytes. |
| OS << "0x!!"; |
| } else { |
| uint32_t BitEnd = IsFullByte ? 8 : BitWidth & 7; |
| for (uint32_t I = 0; I != BitEnd; ++I) { |
| uint32_t Mask = 1U << (BitEnd - 1 - I); |
| if (V.ConcreteMask & Mask) |
| OS << (V.Value & Mask ? '1' : '0'); |
| else { |
| assert((V.Value & Mask) == 0 && |
| "Byte values don't contain undef bits."); |
| OS << '!'; |
| } |
| } |
| assert((V.ConcreteMask & V.TagMask) == V.TagMask); |
| if (V.TagMask) { |
| // Print tags if available. |
| OS << '('; |
| for (uint32_t I = 0; I != BitEnd; ++I) { |
| uint32_t Mask = 1U << (BitEnd - 1 - I); |
| if (V.TagMask & Mask) |
| OS << (V.TagValue & Mask ? '1' : '0'); |
| else |
| OS << '!'; |
| } |
| OS << ')'; |
| } |
| } |
| OS << ' '; |
| }; |
| |
| auto &DL = Ctx.getDataLayout(); |
| unsigned PtrWidthForAS0 = DL.getPointerSizeInBits(0); |
| Type *PtrTy = PointerType::getUnqual(Ctx.getContext()); |
| |
| if (PtrWidthForAS0 % 8 == 0 && BitWidth % PtrWidthForAS0 == 0) { |
| // Try to treat the bytes value as an array of pointers in address space 0. |
| unsigned PtrSize = PtrWidthForAS0 / 8; |
| for (size_t I = 0, E = Val.size(); I != E; I += PtrSize) { |
| ArrayRef<Byte> Slice = ArrayRef(Val).slice(I, PtrSize); |
| if (all_of(Slice, [](const Byte &V) { |
| assert((V.ConcreteMask & V.TagMask) == V.TagMask); |
| return V.TagMask == 255; |
| })) { |
| AnyValue Res = Ctx.fromBytes(Slice, PtrTy); |
| if (Res.isPointer()) { |
| Res.asPointer().print(OS); |
| OS << ' '; |
| continue; |
| } |
| } |
| |
| // Otherwise, fallback into bytes array |
| for (size_t J = 0; J != PtrSize; ++J) |
| PrintByte(Val[I + J]); |
| } |
| } else { |
| for (const Byte &V : Val) |
| PrintByte(V); |
| } |
| } |
| |
| } // namespace llvm::ubi |