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
24enum class ByteKind : uint8_t {
25 // A concrete byte with a known value.
26 Concrete,
27 // A uninitialized byte. Each load from an uninitialized byte yields
28 // a nondeterministic value.
29 Undef,
30 // A poisoned byte. It occurs when the program stores a poison value to
31 // memory,
32 // or when a memory object is dead.
33 Poison,
34};
35
36struct Byte {
37 uint8_t Value;
38 ByteKind Kind : 2;
39 // TODO: provenance
40
41 void set(uint8_t V) {
42 Value = V;
43 Kind = ByteKind::Concrete;
44 }
45};
46
47// TODO: Byte
48enum class StorageKind {
49 Integer,
50 Float,
51 Pointer,
52 Poison,
53 None, // Placeholder for void type
54 Aggregate, // Struct, Array or Vector
55};
56
57/// Tri-state boolean value.
58enum class BooleanKind { False, True, Poison };
59
60class Pointer {
61 // The underlying memory object. It can be null for invalid or dangling
62 // pointers.
63 IntrusiveRefCntPtr<MemoryObject> Obj;
64 // The address of the pointer. The bit width is determined by
65 // DataLayout::getPointerSizeInBits.
66 APInt Address;
67 // TODO: modeling inrange(Start, End) attribute
68
69public:
70 explicit Pointer(const APInt &Address) : Obj(nullptr), Address(Address) {}
71 explicit Pointer(IntrusiveRefCntPtr<MemoryObject> Obj, const APInt &Address)
72 : Obj(std::move(Obj)), Address(Address) {}
73 Pointer getWithNewAddr(const APInt &NewAddr) const {
74 return Pointer(Obj, NewAddr);
75 }
76 static AnyValue null(unsigned BitWidth);
77 void print(raw_ostream &OS) const;
78 const APInt &address() const { return Address; }
79 MemoryObject *getMemoryObject() const { return Obj.get(); }
80};
81
82// Value representation for actual values of LLVM values.
83// We don't model undef values here (except for byte types).
84class [[nodiscard]] AnyValue {
85 StorageKind Kind;
86 union {
87 APInt IntVal;
88 APFloat FloatVal;
89 Pointer PtrVal;
90 std::vector<AnyValue> AggVal;
91 };
92
93 struct PoisonTag {};
94 void destroy();
95
96public:
97 AnyValue() : Kind(StorageKind::None) {}
98 explicit AnyValue(PoisonTag) : Kind(StorageKind::Poison) {}
99 AnyValue(APInt Val) : Kind(StorageKind::Integer), IntVal(std::move(Val)) {}
100 AnyValue(APFloat Val) : Kind(StorageKind::Float), FloatVal(std::move(Val)) {}
101 AnyValue(Pointer Val) : Kind(StorageKind::Pointer), PtrVal(std::move(Val)) {}
102 AnyValue(std::vector<AnyValue> Val)
103 : Kind(StorageKind::Aggregate), AggVal(std::move(Val)) {}
104 AnyValue(const AnyValue &Other);
105 AnyValue(AnyValue &&Other);
106 AnyValue &operator=(const AnyValue &);
107 AnyValue &operator=(AnyValue &&);
108 ~AnyValue() { destroy(); }
109
110 void print(raw_ostream &OS) const;
111
112 static AnyValue poison() { return AnyValue(PoisonTag{}); }
113 static AnyValue boolean(bool Val) { return AnyValue(APInt(1, Val)); }
114 static AnyValue getPoisonValue(Context &Ctx, Type *Ty);
115 static AnyValue getNullValue(Context &Ctx, Type *Ty);
116 static AnyValue getVectorSplat(const AnyValue &Scalar, size_t NumElements);
117
118 bool isNone() const { return Kind == StorageKind::None; }
119 bool isPoison() const { return Kind == StorageKind::Poison; }
120 bool isInteger() const { return Kind == StorageKind::Integer; }
121 bool isFloat() const { return Kind == StorageKind::Float; }
122 bool isPointer() const { return Kind == StorageKind::Pointer; }
123 bool isAggregate() const { return Kind == StorageKind::Aggregate; }
124
125 const APInt &asInteger() const {
126 assert(Kind == StorageKind::Integer && "Expect an integer value");
127 return IntVal;
128 }
129
130 const APFloat &asFloat() const {
131 assert(Kind == StorageKind::Float && "Expect a float value");
132 return FloatVal;
133 }
134
135 const Pointer &asPointer() const {
136 assert(Kind == StorageKind::Pointer && "Expect a pointer value");
137 return PtrVal;
138 }
139
140 const std::vector<AnyValue> &asAggregate() const {
141 assert(Kind == StorageKind::Aggregate &&
142 "Expect an aggregate/vector value");
143 return AggVal;
144 }
145
146 std::vector<AnyValue> &asAggregate() {
147 assert(Kind == StorageKind::Aggregate &&
148 "Expect an aggregate/vector value");
149 return AggVal;
150 }
151
152 // Helper function for C++ 17 structured bindings.
153 template <size_t I> const AnyValue &get() const {
154 assert(Kind == StorageKind::Aggregate &&
155 "Expect an aggregate/vector value");
156 assert(I < AggVal.size() && "Index out of bounds");
157 return AggVal[I];
158 }
159
160 BooleanKind asBoolean() const {
161 if (isPoison())
162 return BooleanKind::Poison;
163 return asInteger().isZero() ? BooleanKind::False : BooleanKind::True;
164 }
165};
166
167inline raw_ostream &operator<<(raw_ostream &OS, const AnyValue &V) {
168 V.print(OS);
169 return OS;
170}
171
172} // namespace llvm::ubi
173
174#endif
175