Skip to content

Commit 822ed70

Browse files
author
Tim Foley
authoredDec 13, 2018
Move mangled name out of IRGlobalValue (shader-slang#752)
* Move mangled name out of IRGlobalValue Previously the `IRGlobalValue` type was used as a root for all IR instructions that can have "linkage," in the sense that a definition in one module can satisfy a use in another module. The mangled symbol name was stored in state directly on each `IRGlobalValue`, which created some complications, and also forced IR instructions that wanted to support linkage to wedge into the hierarchy at that specific point. This change moves the mangled name out into a decoration: either an `IRImportDecoration` or an `IRExportDecoration`, both of which inherit from `IRLinkageDecoration` which exposes the mangled name. This change has a few benefits: * We can now have any kind of instruction be exported/imported, without having to inherit from `IRGlobalValue`. This could potentially let `IRStructType` and `IRWitnessTable` be simplified to just have operand lists instead of dummy chldren as they do today. * We can now easily have "global values" like functions that explicitly *don't* get linkage, instead of using a null or empty mangled name as a marker. * We can use the exact opcode on a linkage decoration to distinguish imports from exports, which could be used to more accurately resolve symbols during the linking step. Other than adding the decorations and making sure that AST->IR lowering adds them, the main changes here are around any code that used `IRGlobalValue`. Variables and parameters of type `IRGlobalValue*` were changed to `IRInst*` easily, so the main challenge was around code that *casts* to `IRGlobalValue*. In cases where a cast to `IRGlobalValue` also performed a test for the mangled name being non-null/non-empty, we simply switched the code to check for the presence of an `IRLinkageDecoration`, since that is the new way of indicating a value with linakge. Most of the serious complications arose in `ir.cpp` around the "linking"/target-specialization and generic specialization steps. The "linking" logic was checking for `IRGlobalValue` to opt into some more complicated cloning logic, and just checking for a linkage decoration here wasn't sufficient since the front-end *does* produce global values without linkage in some cases (e.g., for a function-`static` variable we produce a global variable without linkage). This logic was updated to just check for the cases that used to amount to `IRGlobalValue`s directly by opcode. It might be simpler in the short term to have kept `IRGlobalValue` around to make the existing casts Just Work, but I'm confident that this logic could actually be rewritten for much greater clarity and simplicity and that is the better way forward. The generic specialization logic was using some really messy code to generate a new mangled name to represent the specialized symbol, and then searching for an existing match for that name. The original idea there was that an IR module could include "pre-specialized" versions of certain generics to speed up back-end compilation by eliminating the need to specialize in some cases, but this feature has never been implemented so the overhead here is just a waste. Instead, I moved generic specialization to use a simpler dictionary to map the operands to a `specialize` instruction over to the resulting specialized value. This allows for some simplifications in the name mangling logic, because it no longer needs to figure out how to produce mangled names from IR instructions representing types/values. As part of this change I also overhauled the IR emit logic to produce cleaner output by default, borrowing some of the ideas from the logic in `emit.cpp`. IR values are now automatically given names based on their "name hint" decoration, if any, to make the code easier to follow, and I also made it so that types and literals get collapsed into their use sites in a new "simplified" IR dump mode (which is currently the default, with no way to opt into the other mode without tweaking the code). The resulting IR dumps are much nicer to look at, but as a result the one test that involves IR dumping (`ir/string-literal`) doesn't really test what it used to. One weird issue that came up during testing is that the `transitive-interface` test had previously been producing output that made no sense (that is, the expected output file wasn't really sensible), and somehow these changes were altering its behavior. Changing the test to use `int` values instead of `float` was enough to make the output be what I'd expect, and hand inspection of generating DXBC has me convinced we were compiling the `float` case correctly too. There appears to be some issue around tests with floating-point outputs that we should investigate. * fixup: C++ declaration order
1 parent 76280cf commit 822ed70

18 files changed

+847
-812
lines changed
 

‎source/core/slang-string.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ namespace Slang
4545

4646
// UnownedStringSlice
4747

48+
bool UnownedStringSlice::startsWith(UnownedStringSlice const& other) const
49+
{
50+
UInt thisSize = size();
51+
UInt otherSize = other.size();
52+
53+
if (otherSize > thisSize)
54+
return false;
55+
56+
return UnownedStringSlice(begin(), begin() + otherSize) == other;
57+
}
58+
59+
bool UnownedStringSlice::startsWith(char const* str) const
60+
{
61+
return startsWith(UnownedTerminatedStringSlice(str));
62+
}
63+
64+
4865
bool UnownedStringSlice::endsWith(UnownedStringSlice const& other) const
4966
{
5067
UInt thisSize = size();

‎source/core/slang-string.h

+2
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ namespace Slang
133133
return !(*this == other);
134134
}
135135

136+
bool startsWith(UnownedStringSlice const& other) const;
137+
bool startsWith(char const* str) const;
136138

137139
bool endsWith(UnownedStringSlice const& other) const;
138140
bool endsWith(char const* str) const;

‎source/slang/emit.cpp

+52-36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// emit.cpp
22
#include "emit.h"
33

4+
#include "../core/slang-writer.h"
45
#include "ir-insts.h"
56
#include "ir-restructure.h"
67
#include "ir-restructure-scoping.h"
@@ -2226,17 +2227,9 @@ struct EmitVisitor
22262227

22272228

22282229
// If the instruction has a mangled name, then emit using that.
2229-
if (auto globalValue = as<IRGlobalValue>(inst))
2230+
if(auto linkageDecoration = inst->findDecoration<IRLinkageDecoration>())
22302231
{
2231-
auto mangledName = globalValue->mangledName;
2232-
if (mangledName)
2233-
{
2234-
auto mangledNameText = getText(mangledName);
2235-
if (mangledNameText.Length() != 0)
2236-
{
2237-
return getText(mangledName);
2238-
}
2239-
}
2232+
return linkageDecoration->getMangledName();
22402233
}
22412234

22422235
// Otherwise fall back to a construct temporary name
@@ -3411,7 +3404,7 @@ struct EmitVisitor
34113404
// (which is `func` above), so we need to walk
34123405
// upwards to find it.
34133406
//
3414-
IRGlobalValue* valueForName = func;
3407+
IRInst* valueForName = func;
34153408
for(;;)
34163409
{
34173410
auto parentBlock = as<IRBlock>(valueForName->parent);
@@ -3425,9 +3418,17 @@ struct EmitVisitor
34253418
valueForName = parentGeneric;
34263419
}
34273420

3421+
// If we reach this point, we are assuming that the value
3422+
// has some kind of linkage, and thus a mangled name.
3423+
//
3424+
auto linkageDecoration = valueForName->findDecoration<IRLinkageDecoration>();
3425+
SLANG_ASSERT(linkageDecoration);
3426+
auto mangledName = String(linkageDecoration->getMangledName());
3427+
3428+
34283429
// We will use the `UnmangleContext` utility to
34293430
// help us split the original name into its pieces.
3430-
UnmangleContext um(getText(valueForName->mangledName));
3431+
UnmangleContext um(mangledName);
34313432
um.startUnmangling();
34323433

34333434
// We'll read through the qualified name of the
@@ -5969,13 +5970,16 @@ struct EmitVisitor
59695970
// appropriate decoration to these variables to indicate their
59705971
// purpose.
59715972
//
5972-
if(getText(varDecl->mangledName).StartsWith("gl_"))
5973+
if(auto linkageDecoration = varDecl->findDecoration<IRLinkageDecoration>())
59735974
{
5974-
// The variable represents an OpenGL system value,
5975-
// so we will assume that it doesn't need to be declared.
5976-
//
5977-
// TODO: handle case where we *should* declare the variable.
5978-
return;
5975+
if(linkageDecoration->getMangledName().startsWith("gl_"))
5976+
{
5977+
// The variable represents an OpenGL system value,
5978+
// so we will assume that it doesn't need to be declared.
5979+
//
5980+
// TODO: handle case where we *should* declare the variable.
5981+
return;
5982+
}
59795983
}
59805984
}
59815985

@@ -6363,6 +6367,31 @@ void legalizeTypes(
63636367
TypeLegalizationContext* context,
63646368
IRModule* module);
63656369

6370+
static void dumpIRIfEnabled(
6371+
CompileRequest* compileRequest,
6372+
IRModule* irModule,
6373+
char const* label = nullptr)
6374+
{
6375+
if(compileRequest->shouldDumpIR)
6376+
{
6377+
WriterHelper writer(compileRequest->getWriter(WriterChannel::StdError));
6378+
6379+
if(label)
6380+
{
6381+
writer.put("### ");
6382+
writer.put(label);
6383+
writer.put(":\n");
6384+
}
6385+
6386+
dumpIR(irModule, writer.getWriter());
6387+
6388+
if( label )
6389+
{
6390+
writer.put("###\n");
6391+
}
6392+
}
6393+
}
6394+
63666395
String emitEntryPoint(
63676396
EntryPointRequest* entryPoint,
63686397
ProgramLayout* programLayout,
@@ -6422,22 +6451,15 @@ String emitEntryPoint(
64226451
&sharedContext.extensionUsageTracker);
64236452

64246453
#if 0
6425-
fprintf(stderr, "### CLONED:\n");
6426-
dumpIR(irModule);
6427-
fprintf(stderr, "###\n");
6454+
dumpIRIfEnabled(compileRequest, irModule, "CLONED");
64286455
#endif
64296456

64306457
validateIRModuleIfEnabled(compileRequest, irModule);
64316458

64326459
// If the user specified the flag that they want us to dump
64336460
// IR, then do it here, for the target-specific, but
64346461
// un-specialized IR.
6435-
if (translationUnit->compileRequest->shouldDumpIR)
6436-
{
6437-
ISlangWriter* writer = translationUnit->compileRequest->getWriter(WriterChannel::StdError);
6438-
6439-
dumpIR(irModule, writer);
6440-
}
6462+
dumpIRIfEnabled(compileRequest, irModule);
64416463

64426464
// Next, we need to ensure that the code we emit for
64436465
// the target doesn't contain any operations that would
@@ -6449,9 +6471,7 @@ String emitEntryPoint(
64496471

64506472
// Debugging code for IR transformations...
64516473
#if 0
6452-
fprintf(stderr, "### SPECIALIZED:\n");
6453-
dumpIR(irModule);
6454-
fprintf(stderr, "###\n");
6474+
dumpIRIfEnabled(compileRequest, irModule, "SPECIALIZED");
64556475
#endif
64566476
validateIRModuleIfEnabled(compileRequest, irModule);
64576477

@@ -6466,9 +6486,7 @@ String emitEntryPoint(
64666486

64676487
// Debugging output of legalization
64686488
#if 0
6469-
fprintf(stderr, "### LEGALIZED:\n");
6470-
dumpIR(irModule);
6471-
fprintf(stderr, "###\n");
6489+
dumpIRIfEnabled(compileRequest, irModule, "LEGALIZED");
64726490
#endif
64736491
validateIRModuleIfEnabled(compileRequest, irModule);
64746492

@@ -6480,9 +6498,7 @@ String emitEntryPoint(
64806498
constructSSA(irModule);
64816499

64826500
#if 0
6483-
fprintf(stderr, "### AFTER SSA:\n");
6484-
dumpIR(irModule);
6485-
fprintf(stderr, "###\n");
6501+
dumpIRIfEnabled(compileRequest, irModule, "AFTER SSA");
64866502
#endif
64876503
validateIRModuleIfEnabled(compileRequest, irModule);
64886504

‎source/slang/ir-constexpr.cpp

+4-7
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ struct PropagateConstExprContext
1616
SharedIRBuilder sharedBuilder;
1717
IRBuilder builder;
1818

19-
List<IRGlobalValue*> workList;
20-
HashSet<IRGlobalValue*> onWorkList;
19+
List<IRInst*> workList;
20+
HashSet<IRInst*> onWorkList;
2121

2222
IRBuilder* getBuilder() { return &builder; }
2323

@@ -158,7 +158,7 @@ bool propagateConstExprForward(
158158

159159
void maybeAddToWorkList(
160160
PropagateConstExprContext* context,
161-
IRGlobalValue* gv)
161+
IRInst* gv)
162162
{
163163
if( !context->onWorkList.Contains(gv) )
164164
{
@@ -484,10 +484,7 @@ void propagateConstExpr(
484484

485485
for( auto ii : module->getGlobalInsts() )
486486
{
487-
auto gv = as<IRGlobalValue>(ii);
488-
if (!gv)
489-
continue;
490-
maybeAddToWorkList(&context, gv);
487+
maybeAddToWorkList(&context, ii);
491488
}
492489

493490
// We will iterate applying propagation to one global value at a time

‎source/slang/ir-inst-defs.h

+17-16
Original file line numberDiff line numberDiff line change
@@ -153,23 +153,19 @@ INST(StructType, struct, 0, PARENT)
153153

154154
INST_RANGE(Type, VoidType, StructType)
155155

156-
/*IRGlobalValue*/
156+
/*IRGlobalValueWithCode*/
157+
/* IRGlobalValueWIthParams*/
158+
INST(Func, func, 0, PARENT)
159+
INST(Generic, generic, 0, PARENT)
160+
INST_RANGE(GlobalValueWithParams, Func, Generic)
157161

158-
/*IRGlobalValueWithCode*/
159-
/* IRGlobalValueWIthParams*/
160-
INST(Func, func, 0, PARENT)
161-
INST(Generic, generic, 0, PARENT)
162-
INST_RANGE(GlobalValueWithParams, Func, Generic)
162+
INST(GlobalVar, global_var, 0, 0)
163+
INST(GlobalConstant, global_constant, 0, 0)
164+
INST_RANGE(GlobalValueWithCode, Func, GlobalConstant)
163165

164-
INST(GlobalVar, global_var, 0, 0)
165-
INST(GlobalConstant, global_constant, 0, 0)
166-
INST_RANGE(GlobalValueWithCode, Func, GlobalConstant)
167-
168-
INST(StructKey, key, 0, 0)
169-
INST(GlobalGenericParam, global_generic_param, 0, 0)
170-
INST(WitnessTable, witness_table, 0, 0)
171-
172-
INST_RANGE(GlobalValue, StructType, WitnessTable)
166+
INST(StructKey, key, 0, 0)
167+
INST(GlobalGenericParam, global_generic_param, 0, 0)
168+
INST(WitnessTable, witness_table, 0, 0)
173169

174170
INST(Module, module, 0, PARENT)
175171

@@ -384,7 +380,12 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0)
384380
INST(GloballyCoherentDecoration, globallyCoherent, 0, 0)
385381
INST(PatchConstantFuncDecoration, patchConstantFunc, 1, 0)
386382

387-
INST_RANGE(Decoration, HighLevelDeclDecoration, PatchConstantFuncDecoration)
383+
/* LinkageDecoration */
384+
INST(ImportDecoration, import, 1, 0)
385+
INST(ExportDecoration, export, 1, 0)
386+
INST_RANGE(LinkageDecoration, ImportDecoration, ExportDecoration)
387+
388+
INST_RANGE(Decoration, HighLevelDeclDecoration, ExportDecoration)
388389

389390

390391
//

‎source/slang/ir-insts.h

+42-10
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,37 @@ IR_SIMPLE_DECORATION(ReadNoneDecoration)
227227
IR_SIMPLE_DECORATION(EarlyDepthStencilDecoration)
228228
IR_SIMPLE_DECORATION(GloballyCoherentDecoration)
229229

230+
/// A decoration that marks a value as having linkage.
231+
///
232+
/// A value with linkage is either exported from its module,
233+
/// or will have a definition imported from another module.
234+
/// In either case, it requires a mangled name to use when
235+
/// matching imports and exports.
236+
///
237+
struct IRLinkageDecoration : IRDecoration
238+
{
239+
IR_PARENT_ISA(LinkageDecoration)
240+
241+
IRStringLit* getMangledNameOperand() { return cast<IRStringLit>(getOperand(0)); }
242+
243+
UnownedStringSlice getMangledName()
244+
{
245+
return getMangledNameOperand()->getStringSlice();
246+
}
247+
};
248+
249+
struct IRImportDecoration : IRLinkageDecoration
250+
{
251+
enum { kOp = kIROp_ImportDecoration };
252+
IR_LEAF_ISA(ImportDecoration)
253+
};
254+
255+
struct IRExportDecoration : IRLinkageDecoration
256+
{
257+
enum { kOp = kIROp_ExportDecoration };
258+
IR_LEAF_ISA(ExportDecoration)
259+
};
260+
230261
// An instruction that specializes another IR value
231262
// (representing a generic) to a particular set of generic arguments
232263
// (instructions representing types, witness tables, etc.)
@@ -544,7 +575,7 @@ struct IRWitnessTableEntry : IRInst
544575
// interface. It basically takes the form of a
545576
// map from the required members of the interface
546577
// to the IR values that satisfy those requirements.
547-
struct IRWitnessTable : IRGlobalValue
578+
struct IRWitnessTable : IRInst
548579
{
549580
IRInstList<IRWitnessTableEntry> getEntries()
550581
{
@@ -565,7 +596,7 @@ struct IRUndefined : IRInst
565596

566597
// A global-scope generic parameter (a type parameter, a
567598
// constraint parameter, etc.)
568-
struct IRGlobalGenericParam : IRGlobalValue
599+
struct IRGlobalGenericParam : IRInst
569600
{
570601
IR_LEAF_ISA(GlobalGenericParam)
571602
};
@@ -797,10 +828,6 @@ struct IRBuilder
797828
IRType* getType(
798829
IROp op);
799830

800-
801-
IRWitnessTable* lookupWitnessTable(Name* mangledName);
802-
void registerWitnessTable(IRWitnessTable* table);
803-
804831
/// Create an empty basic block.
805832
///
806833
/// The created block will not be inserted into the current
@@ -1043,6 +1070,15 @@ struct IRBuilder
10431070
addDecoration(value, kIROp_PatchConstantFuncDecoration, patchConstantFunc);
10441071
}
10451072

1073+
void addImportDecoration(IRInst* value, UnownedStringSlice const& mangledName)
1074+
{
1075+
addDecoration(value, kIROp_ImportDecoration, getStringValue(mangledName));
1076+
}
1077+
1078+
void addExportDecoration(IRInst* value, UnownedStringSlice const& mangledName)
1079+
{
1080+
addDecoration(value, kIROp_ExportDecoration, getStringValue(mangledName));
1081+
}
10461082
};
10471083

10481084
// Helper to establish the source location that will be used
@@ -1091,10 +1127,6 @@ IRSpecializationState* createIRSpecializationState(
10911127
void destroyIRSpecializationState(IRSpecializationState* state);
10921128
IRModule* getIRModule(IRSpecializationState* state);
10931129

1094-
IRGlobalValue* getSpecializedGlobalValueForDeclRef(
1095-
IRSpecializationState* state,
1096-
DeclRef<Decl> const& declRef);
1097-
10981130
struct ExtensionUsageTracker;
10991131

11001132
// Clone the IR values reachable from the given entry point

0 commit comments

Comments
 (0)
Please sign in to comment.