1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
#pragma once
#include "cps/Identifier.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Type.h"
#include <cstdint>
#include <functional>
#include <map>
#include <string>
#include <utility>
#include <vector>
namespace cps {
struct RecordFieldDecl;
// Enum ordinals are 0-based: DECLARE zero-init and array memset(0) then
// produce the first enumerator, a valid value.
inline constexpr int64_t kEnumOrdinalBase = 0;
// LLVM struct name prefix for record types: %record.NAME
inline constexpr const char *kRecordStructPrefix = "record.";
enum class TypeKind {
Integer,
Real,
Boolean,
Char,
Void,
Enum,
Record,
Pointer,
CScalar,
CPointer
};
// ---- Per-kind payloads (filled only for the matching TypeKind) ----
struct RecordFieldInfo {
std::string TypeName;
unsigned Index = 0; // struct field index
bool IsPublic = true;
};
struct RecordPayload {
std::vector<std::pair<std::string, RecordFieldInfo>> Fields; // declaration order
bool Invalid = false; // a field was rejected and skipped
const RecordFieldInfo *findField(const std::string &Name) const;
};
struct EnumPayload {
std::vector<std::string> ValueNames; // ordinal = position + kEnumOrdinalBase
};
struct PointerPayload {
std::string PointeeTypeName;
};
// A bare enum value name, resolvable in expression position.
struct EnumConstant {
std::string TypeName;
int64_t Ordinal = 0;
};
struct TypeInfo {
std::string Name;
llvm::Type *LLVMType = nullptr;
TypeKind Kind = TypeKind::Void;
bool IsClass = false;
RecordPayload Record; // Kind == Record only
EnumPayload Enum; // Kind == Enum only
PointerPayload Pointer; // Kind == Pointer only
// C aliases share an identity even when their pseudocode names differ.
std::string CIdentity;
unsigned CRank = 0;
bool CSigned = false;
std::string COutputFormat;
bool isC() const;
bool sameType(const TypeInfo *Other) const;
bool isIntegral() const;
bool isReal() const;
bool isBoolean() const;
bool isString() const;
bool isChar() const;
bool isVoid() const;
bool isEnum() const;
bool isRecord() const;
bool isPointer() const;
// Enum/Record/Pointer: type identity is the NAME; representation-level
// coercion must never be reached without a prior name-level check.
bool isUserKind() const;
// OUTPUT admissibility, derived from Kind (enums print their ordinal).
bool isOutputtable() const;
// Checked payload accessors: nullptr unless Kind matches.
const RecordPayload *asRecord() const;
const EnumPayload *asEnum() const;
const PointerPayload *asPointer() const;
};
struct SymbolInfo {
llvm::Value *Storage = nullptr;
std::string TypeName;
bool IsArray = false;
llvm::Constant *ConstantValue = nullptr; // Compile-time scalar CONSTANT value, if available.
unsigned ScopeID = 0;
bool IsReference = false; // Storage is a slot containing the current object address.
bool IsGlobal = false;
bool ReadOnly = false; // Owned STRING constants use storage instead of ConstantValue.
bool isConstant() const { return ConstantValue || ReadOnly; }
};
class TypeSystem {
llvm::LLVMContext &Context;
IdentifierMap<TypeInfo> Types;
IdentifierMap<EnumConstant> EnumConstants;
std::function<bool(const std::string &)> Visible;
std::function<const TypeInfo *(const std::string &)> ResolveMiss;
void registerBuiltins();
TypeInfo *registerType(const std::string &TypeName,
llvm::Type *LLVMType,
TypeKind Kind);
const TypeInfo *importCType(const std::string &Expression, std::string &Err);
public:
explicit TypeSystem(llvm::LLVMContext &Ctx);
void setVisibility(std::function<bool(const std::string &)> Predicate) { Visible = std::move(Predicate); }
void setResolveMiss(std::function<const TypeInfo *(const std::string &)> Hook) { ResolveMiss = std::move(Hook); }
const TypeInfo *resolve(const std::string &TypeName) const;
// Two-pass user-type registration (pass A then pass B, both in source
// order). Pass A reserves every name so pointer pointees may forward-
// reference; pass B fills definitions. On failure Err carries the
// diagnostic and nothing is (further) registered.
bool declareUserType(const std::string &Name, TypeKind Kind, std::string &Err,
bool IsClass = false);
bool defineEnum(const std::string &Name,
const std::vector<std::string> &Values,
std::string &Err);
bool defineRecord(const std::string &Name,
const std::vector<RecordFieldDecl> &Fields,
std::string &Err);
bool definePointer(const std::string &Name,
const std::string &PointeeName,
std::string &Err);
bool defineCAlias(const std::string &Name, const std::string &Expression,
std::string &Err);
bool sameType(const std::string &Left, const std::string &Right) const;
const TypeInfo *promoteCScalar(const TypeInfo *Info) const;
// Follows pseudocode pointer types to the type they finally point to:
// Info itself when it is not one, nullptr for a cycle (TYPE P = ^P).
const TypeInfo *pointerBase(const TypeInfo *Info) const;
// A TYPE record (not a CLASS) whose fields are C types, C structs or
// pointer types to either. Otherwise Why, if given, names the first
// offending field and its type.
bool isCStruct(const TypeInfo *Info, std::string *Why = nullptr) const;
// Crosses the EXTERN "C" boundary: a non-void C type, a C struct or a
// pointer type to either.
bool isExternType(const TypeInfo *Info) const;
const TypeInfo *commonCScalar(const TypeInfo *Left, const TypeInfo *Right) const;
const EnumConstant *lookupEnumConstant(const std::string &Name) const;
llvm::Type *getLLVMType(const std::string &TypeName) const;
llvm::Constant *getZeroValue(const std::string &TypeName) const;
// Target-independent sizeof: a null-GEP+ptrtoint i64 constant the backend
// folds with the *real* target's DataLayout. The module carries no
// DataLayout string, so sizes must never be computed host-side.
llvm::Constant *getSizeOfConstant(llvm::Type *Ty) const;
};
} // namespace cps
|