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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
// slang-compile-request.h
#pragma once
//
// This file contains the `FrontEndCompileRequest` type
// and the types that it is built from (such as
// `TranslationUnitRequest`). These types are used
// whenever the Slang front-end is invoked to compile
// a module (or, in some cases, one or more modules)
// from source code to a checked AST and Slang IR.
//
// Note that the `EndToEndCompileRequest` type has its
// own header: `slang-end-to-end-request.h`.
//
#include "../compiler-core/slang-artifact.h"
#include "../compiler-core/slang-source-loc.h"
#include "../core/slang-smart-pointer.h"
#include "../core/slang-std-writers.h"
#include "slang-compiler-fwd.h"
#include "slang-diagnostics.h"
#include "slang-module.h"
#include "slang-preprocessor.h"
#include "slang-profile.h"
#include "slang-session.h"
#include "slang-translation-unit.h"
namespace Slang
{
/// Shared functionality between front- and back-end compile requests.
///
/// This is the base class for both `FrontEndCompileRequest` and
/// `BackEndCompileRequest`, and allows a small number of parts of
/// the compiler to be easily invocable from either front-end or
/// back-end work.
///
class CompileRequestBase : public RefObject
{
// TODO: We really shouldn't need this type in the long run.
// The few places that rely on it should be refactored to just
// depend on the underlying information (a linkage and a diagnostic
// sink) directly.
//
// The flags to control dumping and validation of IR should be
// moved to some kind of shared settings/options `struct` that
// both front-end and back-end requests can store.
public:
Session* getSession();
Linkage* getLinkage() { return m_linkage; }
DiagnosticSink* getSink() { return m_sink; }
SourceManager* getSourceManager() { return getLinkage()->getSourceManager(); }
NamePool* getNamePool() { return getLinkage()->getNamePool(); }
ISlangFileSystemExt* getFileSystemExt() { return getLinkage()->getFileSystemExt(); }
SlangResult loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob)
{
return getLinkage()->loadFile(path, outPathInfo, outBlob);
}
protected:
CompileRequestBase(Linkage* linkage, DiagnosticSink* sink);
private:
Linkage* m_linkage = nullptr;
DiagnosticSink* m_sink = nullptr;
};
/// A request for the front-end to find and validate an entry-point function
struct FrontEndEntryPointRequest : RefObject
{
public:
/// Create a request for an entry point.
FrontEndEntryPointRequest(
FrontEndCompileRequest* compileRequest,
int translationUnitIndex,
Name* name,
Profile profile);
/// Get the parent front-end compile request.
FrontEndCompileRequest* getCompileRequest() { return m_compileRequest; }
/// Get the translation unit that contains the entry point.
TranslationUnitRequest* getTranslationUnit();
/// Get the name of the entry point to find.
Name* getName() { return m_name; }
/// Get the stage that the entry point is to be compiled for
Stage getStage() { return m_profile.getStage(); }
/// Get the profile that the entry point is to be compiled for
Profile getProfile() { return m_profile; }
/// Get the index to the translation unit
int getTranslationUnitIndex() const { return m_translationUnitIndex; }
private:
// The parent compile request
FrontEndCompileRequest* m_compileRequest;
// The index of the translation unit that will hold the entry point
int m_translationUnitIndex;
// The name of the entry point function to look for
Name* m_name;
// The profile to compile for (including stage)
Profile m_profile;
};
/// A request to compile source code to an AST + IR.
class FrontEndCompileRequest : public CompileRequestBase
{
public:
/// Note that writers can be parsed as nullptr to disable output,
/// and individual channels set to null to disable them
FrontEndCompileRequest(Linkage* linkage, StdWriters* writers, DiagnosticSink* sink);
int addEntryPoint(int translationUnitIndex, String const& name, Profile entryPointProfile);
// Translation units we are being asked to compile
List<RefPtr<TranslationUnitRequest>> translationUnits;
// Additional modules that needs to be made visible to `import` while checking.
const LoadedModuleDictionary* additionalLoadedModules = nullptr;
RefPtr<TranslationUnitRequest> getTranslationUnit(UInt index)
{
return translationUnits[index];
}
// If true will serialize and de-serialize with debug information
bool verifyDebugSerialization = false;
CompilerOptionSet optionSet;
List<RefPtr<FrontEndEntryPointRequest>> m_entryPointReqs;
List<RefPtr<FrontEndEntryPointRequest>> const& getEntryPointReqs() { return m_entryPointReqs; }
UInt getEntryPointReqCount() { return m_entryPointReqs.getCount(); }
FrontEndEntryPointRequest* getEntryPointReq(UInt index) { return m_entryPointReqs[index]; }
void parseTranslationUnit(TranslationUnitRequest* translationUnit);
// Perform primary semantic checking on all
// of the translation units in the program
void checkAllTranslationUnits();
void checkEntryPoints();
void generateIR();
SlangResult executeActionsInner();
/// Add a translation unit to be compiled.
///
/// @param language The source language that the translation unit will use (e.g.,
/// `SourceLanguage::Slang`
/// @param moduleName The name that will be used for the module compile from the translation
/// unit.
///
/// If moduleName is passed as nullptr a module name is generated.
/// If all translation units in a compile request use automatically generated
/// module names, then they are guaranteed not to conflict with one another.
///
/// @return The zero-based index of the translation unit in this compile request.
int addTranslationUnit(SourceLanguage language, Name* moduleName);
int addTranslationUnit(TranslationUnitRequest* translationUnit);
void addTranslationUnitSourceArtifact(int translationUnitIndex, IArtifact* sourceArtifact);
void addTranslationUnitSourceBlob(
int translationUnitIndex,
String const& path,
ISlangBlob* sourceBlob);
void addTranslationUnitSourceFile(int translationUnitIndex, String const& path);
/// Get a component type that represents the global scope of the compile request.
ComponentType* getGlobalComponentType() { return m_globalComponentType; }
/// Get a component type that represents the global scope of the compile request, plus the
/// requested entry points.
ComponentType* getGlobalAndEntryPointsComponentType()
{
return m_globalAndEntryPointsComponentType;
}
List<RefPtr<ComponentType>> const& getUnspecializedEntryPoints()
{
return m_unspecializedEntryPoints;
}
/// Does the code we are compiling represent part of the Slang core module?
bool m_isCoreModuleCode = false;
Name* m_defaultModuleName = nullptr;
/// The irDumpOptions
IRDumpOptions m_irDumpOptions;
/// An "extra" entry point that was added via a library reference
struct ExtraEntryPointInfo
{
Name* name;
Profile profile;
String mangledName;
};
/// A list of "extra" entry points added via a library reference
List<ExtraEntryPointInfo> m_extraEntryPoints;
private:
/// A component type that includes only the global scopes of the translation unit(s) that were
/// compiled.
RefPtr<ComponentType> m_globalComponentType;
/// A component type that extends the global scopes with all of the entry points that were
/// specified.
RefPtr<ComponentType> m_globalAndEntryPointsComponentType;
List<RefPtr<ComponentType>> m_unspecializedEntryPoints;
RefPtr<StdWriters> m_writers;
};
/// Handlers for preprocessor callbacks to use when doing ordinary front-end compilation
struct FrontEndPreprocessorHandler : PreprocessorHandler
{
public:
FrontEndPreprocessorHandler(
Module* module,
ASTBuilder* astBuilder,
DiagnosticSink* sink,
TranslationUnitRequest* translationUnit)
: m_module(module)
, m_astBuilder(astBuilder)
, m_sink(sink)
, m_translationUnit(translationUnit)
{
}
protected:
Module* m_module;
ASTBuilder* m_astBuilder;
DiagnosticSink* m_sink;
TranslationUnitRequest* m_translationUnit = nullptr;
// The first task that this handler tries to deal with is
// capturing all the files on which a module is dependent.
//
// That information is exposed through public APIs and used
// by applications to decide when they need to "hot reload"
// their shader code.
//
void handleFileDependency(SourceFile* sourceFile) SLANG_OVERRIDE
{
m_module->addFileDependency(sourceFile);
m_translationUnit->addIncludedSourceFileIfNotExist(sourceFile);
}
// The second task that this handler deals with is detecting
// whether any macro values were set in a given source file
// that are semantically relevant to other stages of compilation.
//
void handleEndOfTranslationUnit(Preprocessor* preprocessor) SLANG_OVERRIDE
{
// We look at the preprocessor state after reading the entire
// source file/string, in order to see if any macros have been
// set that should be considered semantically relevant for
// later stages of compilation.
//
// Note: Checking the macro environment *after* preprocessing is complete
// means that we can treat macros introduced via `-D` options or the API
// equivalently to macros introduced via `#define`s in user code.
//
// For now, the only case of semantically-relevant macros we need to worrry
// about are the NVAPI macros used to establish the register/space to use.
//
static const char* kNVAPIRegisterMacroName = "NV_SHADER_EXTN_SLOT";
static const char* kNVAPISpaceMacroName = "NV_SHADER_EXTN_REGISTER_SPACE";
// For NVAPI use, the `NV_SHADER_EXTN_SLOT` macro is required to be defined.
//
String nvapiRegister;
SourceLoc nvapiRegisterLoc;
if (!SLANG_FAILED(findMacroValue(
preprocessor,
kNVAPIRegisterMacroName,
nvapiRegister,
nvapiRegisterLoc)))
{
// In contrast, NVAPI can be used without defining `NV_SHADER_EXTN_REGISTER_SPACE`,
// which effectively defaults to `space0`.
//
String nvapiSpace = "space0";
SourceLoc nvapiSpaceLoc;
findMacroValue(preprocessor, kNVAPISpaceMacroName, nvapiSpace, nvapiSpaceLoc);
// We are going to store the values of these macros on the AST-level `ModuleDecl`
// so that they will be available to later processing stages.
//
auto moduleDecl = m_module->getModuleDecl();
if (auto existingModifier = moduleDecl->findModifier<NVAPISlotModifier>())
{
// If there is already a modifier attached to the module (perhaps
// because of preprocessing a different source file, or because
// of settings established via command-line options), then we
// need to validate that the values being set in this file
// match those already set (or else there is likely to be
// some kind of error in the user's code).
//
_validateNVAPIMacroMatch(
kNVAPIRegisterMacroName,
existingModifier->registerName,
nvapiRegister,
nvapiRegisterLoc);
_validateNVAPIMacroMatch(
kNVAPISpaceMacroName,
existingModifier->spaceName,
nvapiSpace,
nvapiSpaceLoc);
}
else
{
// If there is no existing modifier on the module, then we
// take responsibility for adding one, based on the macro
// values we saw.
//
auto modifier = m_astBuilder->create<NVAPISlotModifier>();
modifier->loc = nvapiRegisterLoc;
modifier->registerName = nvapiRegister;
modifier->spaceName = nvapiSpace;
addModifier(moduleDecl, modifier);
}
}
}
/// Validate that a re-defintion of an NVAPI-related macro matches any previous definition
void _validateNVAPIMacroMatch(
char const* macroName,
String const& existingValue,
String const& newValue,
SourceLoc loc)
{
if (existingValue != newValue)
{
m_sink->diagnose(
loc,
Diagnostics::nvapiMacroMismatch,
macroName,
existingValue,
newValue);
}
}
};
} // namespace Slang
|