1//===--- Value.h - Value Representation for llubi ---------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_TOOLS_LLUBI_VALUE_H
10#define LLVM_TOOLS_LLUBI_VALUE_H
11
12#include "llvm/ADT/APFloat.h"
13#include "llvm/ADT/APInt.h"
14#include "llvm/ADT/IntrusiveRefCntPtr.h"
15#include "llvm/IR/Type.h"
16#include "llvm/Support/raw_ostream.h"
17
18namespace llvm::ubi {
19
20class MemoryObject;
21class Context;
22class AnyValue;
23
24/// Representation of a byte in memory.
25/// How to interpret the byte per bit:
26/// - If the concrete mask bit is 0, the bit is either undef or poison. The
27/// value bit indicates whether it is undef.
28/// - If the concrete mask bit is 1, the bit is a concrete value. The value bit
29/// stores the concrete bit value.
30struct Byte {
31 uint8_t ConcreteMask;
32 uint8_t Value;
33 // TODO: captured capabilities of pointers.
34
35 static Byte poison() { return Byte{.ConcreteMask: 0, .Value: 0}; }
36 static Byte undef() { return Byte{.ConcreteMask: 0, .Value: 255}; }
37 static Byte concrete(uint8_t Val) { return Byte{.ConcreteMask: 255, .Value: Val}; }
38
39 void zeroBits(uint8_t Mask) {
40 ConcreteMask |= Mask;
41 Value &= ~Mask;
42 }
43
44 void poisonBits(uint8_t Mask) {
45 ConcreteMask &= ~Mask;
46 Value &= ~Mask;
47 }
48
49 void undefBits(uint8_t Mask) {
50 ConcreteMask &= ~Mask;
51 Value |= Mask;
52 }
53
54 void writeBits(uint8_t Mask, uint8_t Val) {
55 ConcreteMask |= Mask;
56 Value = (Value & ~Mask) | (Val & Mask);
57 }
58
59 /// Returns a logical byte that is part of two adjacent bytes.
60 /// Example with ShAmt = 5:
61 /// | Low | High |
62 /// LSB | 0 1 0 1 0 1 0 1 | 0 0 0 0 1 1 1 1 | MSB
63 /// Result = | 1 0 1 0 0 0 0 1 |
64 static Byte fshr(const Byte &Low, const Byte &High, uint32_t ShAmt) {
65 return Byte{.ConcreteMask: static_cast<uint8_t>(
66 (Low.ConcreteMask | (High.ConcreteMask << 8)) >> ShAmt),
67 .Value: static_cast<uint8_t>((Low.Value | (High.Value << 8)) >> ShAmt)};
68 }
69
70 Byte lshr(uint8_t Shift) const {
71 return Byte{.ConcreteMask: static_cast<uint8_t>(ConcreteMask >> Shift),
72 .Value: static_cast<uint8_t>(Value >> Shift)};
73 }
74};
75
76// TODO: Byte
77enum class StorageKind {
78 Integer,
79 Float,
80 Pointer,
81 Poison,
82 None, // Placeholder for void type
83 Aggregate, // Struct, Array or Vector
84};
85
86/// Tri-state boolean value.
87enum class BooleanKind { False, True, Poison };
88
89class Pointer {
90 // The underlying memory object. It can be null for invalid or dangling
91 // pointers.
92 IntrusiveRefCntPtr<MemoryObject> Obj;
93 // The address of the pointer. The bit width is determined by
94 // DataLayout::getPointerSizeInBits.
95 APInt Address;
96 // TODO: modeling inrange(Start, End) attribute
97
98public:
99 explicit Pointer(const APInt &Address) : Obj(nullptr), Address(Address) {}
100 explicit Pointer(IntrusiveRefCntPtr<MemoryObject> Obj, const APInt &Address)
101 : Obj(std::move(Obj)), Address(Address) {}
102 Pointer getWithNewAddr(const APInt &NewAddr) const {
103 return Pointer(Obj, NewAddr);
104 }
105 static AnyValue null(unsigned BitWidth);
106 void print(raw_ostream &OS) const;
107 const APInt &address() const { return Address; }
108 MemoryObject *getMemoryObject() const { return Obj.get(); }
109};
110
111// Value representation for actual values of LLVM values.
112// We don't model undef values here (except for byte types).
113class [[nodiscard]] AnyValue {
114 StorageKind Kind;
115 union {
116 APInt IntVal;
117 APFloat FloatVal;
118 Pointer PtrVal;
119 std::vector<AnyValue> AggVal;
120 };
121
122 struct PoisonTag {};
123 void destroy();
124
125public:
126 AnyValue() : Kind(StorageKind::None) {}
127 explicit AnyValue(PoisonTag) : Kind(StorageKind::Poison) {}
128 AnyValue(APInt Val) : Kind(StorageKind::Integer), IntVal(std::move(Val)) {}
129 AnyValue(APFloat Val) : Kind(StorageKind::Float), FloatVal(std::move(Val)) {}
130 AnyValue(Pointer Val) : Kind(StorageKind::Pointer), PtrVal(std::move(Val)) {}
131 AnyValue(std::vector<AnyValue> Val)
132 : Kind(StorageKind::Aggregate), AggVal(std::move(Val)) {}
133 AnyValue(const AnyValue &Other);
134 AnyValue(AnyValue &&Other);
135 AnyValue &operator=(const AnyValue &);
136 AnyValue &operator=(AnyValue &&);
137 ~AnyValue() { destroy(); }
138
139 void print(raw_ostream &OS) const;
140
141 static AnyValue poison() { return AnyValue(PoisonTag{}); }
142 static AnyValue boolean(bool Val) { return AnyValue(APInt(1, Val)); }
143 static AnyValue getPoisonValue(Context &Ctx, Type *Ty);
144 static AnyValue getNullValue(Context &Ctx, Type *Ty);
145 static AnyValue getVectorSplat(const AnyValue &Scalar, size_t NumElements);
146
147 bool isNone() const { return Kind == StorageKind::None; }
148 bool isPoison() const { return Kind == StorageKind::Poison; }
149 bool isInteger() const { return Kind == StorageKind::Integer; }
150 bool isFloat() const { return Kind == StorageKind::Float; }
151 bool isPointer() const { return Kind == StorageKind::Pointer; }
152 bool isAggregate() const { return Kind == StorageKind::Aggregate; }
153
154 const APInt &asInteger() const {
155 assert(Kind == StorageKind::Integer && "Expect an integer value");
156 return IntVal;
157 }
158
159 const APFloat &asFloat() const {
160 assert(Kind == StorageKind::Float && "Expect a float value");
161 return FloatVal;
162 }
163
164 const Pointer &asPointer() const {
165 assert(Kind == StorageKind::Pointer && "Expect a pointer value");
166 return PtrVal;
167 }
168
169 const std::vector<AnyValue> &asAggregate() const {
170 assert(Kind == StorageKind::Aggregate &&
171 "Expect an aggregate/vector value");
172 return AggVal;
173 }
174
175 std::vector<AnyValue> &asAggregate() {
176 assert(Kind == StorageKind::Aggregate &&
177 "Expect an aggregate/vector value");
178 return AggVal;
179 }
180
181 // Helper function for C++ 17 structured bindings.
182 template <size_t I> const AnyValue &get() const {
183 assert(Kind == StorageKind::Aggregate &&
184 "Expect an aggregate/vector value");
185 assert(I < AggVal.size() && "Index out of bounds");
186 return AggVal[I];
187 }
188
189 BooleanKind asBoolean() const {
190 if (isPoison())
191 return BooleanKind::Poison;
192 return asInteger().isZero() ? BooleanKind::False : BooleanKind::True;
193 }
194};
195
196inline raw_ostream &operator<<(raw_ostream &OS, const AnyValue &V) {
197 V.print(OS);
198 return OS;
199}
200
201} // namespace llvm::ubi
202
203#endif
204