summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-string.h9
-rw-r--r--source/slang/bytecode.cpp4
-rw-r--r--source/slang/emit.cpp298
-rw-r--r--source/slang/ir-insts.h2
-rw-r--r--source/slang/ir.cpp595
-rw-r--r--source/slang/ir.h63
-rw-r--r--source/slang/lower-to-ir.cpp9
7 files changed, 944 insertions, 36 deletions
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index 23ab54c23..3f2b85d28 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -134,6 +134,15 @@ namespace Slang
struct UnownedStringSlice
{
public:
+ UnownedStringSlice()
+ : beginData(0)
+ , endData(0)
+ {}
+
+ UnownedStringSlice(char const* b, char const* e)
+ : beginData(b)
+ , endData(e)
+ {}
char const* begin() const
{
diff --git a/source/slang/bytecode.cpp b/source/slang/bytecode.cpp
index 854d9bf34..8d8d609b9 100644
--- a/source/slang/bytecode.cpp
+++ b/source/slang/bytecode.cpp
@@ -956,7 +956,7 @@ BytecodeGenerationPtr<BCModule> generateBytecodeForModule(
// for the module, where the registers represent the
// values being computed at the global scope.
UInt symbolCount = 0;
- for( auto gv : irModule->globalValues )
+ for( auto gv = irModule->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
{
Int globalID = Int(symbolCount++);
@@ -975,7 +975,7 @@ BytecodeGenerationPtr<BCModule> generateBytecodeForModule(
bcModule->symbolCount = symbolCount;
bcModule->symbols = bcSymbols;
- for( auto gv : irModule->globalValues )
+ for( auto gv = irModule->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
{
UInt symbolIndex = *context->mapInstToLocalID.TryGetValue(gv);
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 2d42a79ed..938d54d36 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -460,6 +460,12 @@ struct EmitVisitor
Emit(text.begin(), text.end());
}
+ void emit(UnownedStringSlice const& text)
+ {
+ Emit(text.begin(), text.end());
+ }
+
+
void emit(Name* name)
{
emit(getText(name));
@@ -3546,6 +3552,14 @@ struct EmitVisitor
switch(info.kind)
{
case LayoutResourceKind::Uniform:
+ // Explicit offsets require a GLSL extension.
+ //
+ // TODO: We really need to fix this so that we
+ // only output an explicit offset for things
+ // that are layed out differently than they
+ // would normally be...
+ requireGLSLExtension("GL_ARB_enhanced_layouts");
+
Emit("layout(offset = ");
Emit(info.index);
Emit(")\n");
@@ -4466,6 +4480,165 @@ emitDeclImpl(decl, nullptr);
emit(" = ");
}
+ class UnmangleContext
+ {
+ private:
+ char const* cursor_ = nullptr;
+ char const* begin_ = nullptr;
+ char const* end_ = nullptr;
+
+ bool isDigit(char c)
+ {
+ return (c >= '0') && (c <= '9');
+ }
+
+ char peek()
+ {
+ return *cursor_;
+ }
+
+ char get()
+ {
+ return *cursor_++;
+ }
+
+ void expect(char c)
+ {
+ if(peek() == c)
+ {
+ get();
+ }
+ else
+ {
+ // ERROR!
+ SLANG_UNEXPECTED("mangled name error");
+ }
+ }
+
+ void expect(char const* str)
+ {
+ while(char c = *str++)
+ expect(c);
+ }
+
+ public:
+ UnmangleContext()
+ {}
+
+ UnmangleContext(String const& str)
+ : cursor_(str.begin())
+ , begin_(str.begin())
+ , end_(str.end())
+ {}
+
+ // Call at the beginning of a mangled name,
+ // to strip off the main prefix
+ void startUnmangling()
+ {
+ expect("_S");
+ }
+
+ int readCount()
+ {
+ int c = peek();
+ if(!isDigit(c))
+ {
+ SLANG_UNEXPECTED("bad name mangling");
+ return 0;
+ }
+ get();
+
+ if(c == '0')
+ return 0;
+
+ int count = 0;
+ for(;;)
+ {
+ count = count*10 + c - '0';
+ c = peek();
+ if(!isDigit(c))
+ return count;
+
+ get();
+ }
+ }
+
+ UnownedStringSlice readSimpleName()
+ {
+ UnownedStringSlice result;
+ for(;;)
+ {
+ int c = peek();
+ if(!isDigit(c))
+ return result;
+
+ // Read the length part
+ int count = readCount();
+ if(count > (end_ - cursor_))
+ {
+ SLANG_UNEXPECTED("bad name mangling");
+ return result;
+ }
+
+ result = UnownedStringSlice(cursor_, cursor_ + count);
+ cursor_ += count;
+ }
+ }
+ };
+
+ void emitIntrinsicCallExpr(
+ EmitContext* context,
+ IRCall* inst,
+ IRFunc* func)
+ {
+ // TODO: we need to inspect the mangled name,
+ // and construct a suitable expression from it...
+
+ UnmangleContext um(func->mangledName);
+
+ um.startUnmangling();
+
+ auto name = um.readSimpleName();
+
+ // TODO: need to detect if name represents
+ // a member function, etc.
+
+ emit(name);
+ emit("(");
+ UInt argCount = inst->getArgCount();
+ for( UInt aa = 1; aa < argCount; ++aa )
+ {
+ if(aa != 1) emit(", ");
+ emitIROperand(context, inst->getArg(aa));
+ }
+ emit(")");
+ }
+
+ void emitIRCallExpr(
+ EmitContext* context,
+ IRCall* inst)
+ {
+ // We want to detect any call to an intrinsic operation,
+ // that we can emit it directly without mangling, etc.
+ auto funcValue = inst->getArg(0);
+ if(auto irFunc = asTargetIntrinsic(context, funcValue))
+ {
+ emitIntrinsicCallExpr(context, inst, irFunc);
+ }
+ else
+ {
+ emitIROperand(context, funcValue);
+ emit("(");
+ UInt argCount = inst->getArgCount();
+ for( UInt aa = 1; aa < argCount; ++aa )
+ {
+ if(aa != 1) emit(", ");
+ emitIROperand(context, inst->getArg(aa));
+ }
+ emit(")");
+ }
+ }
+
void emitIRInstExpr(
EmitContext* context,
IRValue* value)
@@ -4544,19 +4717,20 @@ emitDeclImpl(decl, nullptr);
#define CASE(OPCODE, OP) \
case OPCODE: \
emitIROperand(context, inst->getArg(0)); \
- emit("" #OP " "); \
+ emit(" " #OP " "); \
emitIROperand(context, inst->getArg(1)); \
break
CASE(kIROp_Add, +);
CASE(kIROp_Sub, -);
- CASE(kIROp_Mul, *);
CASE(kIROp_Div, /);
CASE(kIROp_Mod, %);
CASE(kIROp_Lsh, <<);
CASE(kIROp_Rsh, >>);
+ // TODO: Need to pull out component-wise
+ // comparison cases for matrices/vectors
CASE(kIROp_Eql, ==);
CASE(kIROp_Neq, !=);
CASE(kIROp_Greater, >);
@@ -4573,6 +4747,30 @@ emitDeclImpl(decl, nullptr);
#undef CASE
+ // Component-wise ultiplication needs to be special cased,
+ // because GLSL uses infix `*` to express inner product
+ // when working with matrices.
+ case kIROp_Mul:
+ // Are we targetting GLSL, and is this a matrix product?
+ if(getTarget(context) == CodeGenTarget::GLSL
+ && inst->type->As<MatrixExpressionType>())
+ {
+ emit("matrixCompMult(");
+ emitIROperand(context, inst->getArg(0));
+ emit(", ");
+ emitIROperand(context, inst->getArg(1));
+ emit(")");
+ }
+ else
+ {
+ // Default handling is to just rely on infix
+ // `operator*`.
+ emitIROperand(context, inst->getArg(0));
+ emit(" * ");
+ emitIROperand(context, inst->getArg(1));
+ }
+ break;
+
case kIROp_Not:
{
if (inst->getType()->Equals(getSession()->getBoolType()))
@@ -4624,15 +4822,7 @@ emitDeclImpl(decl, nullptr);
case kIROp_Call:
{
- emitIROperand(context, inst->getArg(0));
- emit("(");
- UInt argCount = inst->getArgCount();
- for( UInt aa = 1; aa < argCount; ++aa )
- {
- if(aa != 1) emit(", ");
- emitIROperand(context, inst->getArg(aa));
- }
- emit(")");
+ emitIRCallExpr(context, (IRCall*)inst);
}
break;
@@ -4666,11 +4856,28 @@ emitDeclImpl(decl, nullptr);
case kIROp_Mul_Vector_Matrix:
case kIROp_Mul_Matrix_Vector:
case kIROp_Mul_Matrix_Matrix:
- emit("mul(");
- emitIROperand(context, inst->getArg(0));
- emit(", ");
- emitIROperand(context, inst->getArg(1));
- emit(")");
+ if(getTarget(context) == CodeGenTarget::GLSL)
+ {
+ // GLSL expresses inner-product multiplications
+ // with the ordinary infix `*` operator.
+ //
+ // Note that the order of the operands is reversed
+ // compared to HLSL (and Slang's internal representation)
+ // because the notion of what is a "row" vs. a "column"
+ // is reversed between HLSL/Slang and GLSL.
+ //
+ emitIROperand(context, inst->getArg(1));
+ emit(" * ");
+ emitIROperand(context, inst->getArg(0));
+ }
+ else
+ {
+ emit("mul(");
+ emitIROperand(context, inst->getArg(0));
+ emit(", ");
+ emitIROperand(context, inst->getArg(1));
+ emit(")");
+ }
break;
case kIROp_swizzle:
@@ -5201,6 +5408,7 @@ emitDeclImpl(decl, nullptr);
return nullptr;
}
+#if 0
void emitGLSLEntryPointFunc(
EmitContext* context,
IRFunc* func)
@@ -5274,6 +5482,7 @@ emitDeclImpl(decl, nullptr);
emit("}\n");
}
+#endif
bool isEntryPoint(IRFunc* func)
{
@@ -5296,10 +5505,31 @@ emitDeclImpl(decl, nullptr);
return !isDefinition(func);
}
+ // Check whether a given value names a target intrinsic,
+ // and return the IR function representing the instrinsic
+ // if it does.
+ IRFunc* asTargetIntrinsic(
+ EmitContext* ctxt,
+ IRValue* value)
+ {
+ if(!value)
+ return nullptr;
+
+ if(value->op != kIROp_Func)
+ return nullptr;
+
+ IRFunc* func = (IRFunc*) value;
+ if(!isTargetIntrinsic(ctxt, func))
+ return nullptr;
+
+ return func;
+ }
+
void emitIRFunc(
EmitContext* context,
IRFunc* func)
{
+#if 0
if( getTarget(context) == CodeGenTarget::GLSL
&& isEntryPoint(func) )
{
@@ -5312,7 +5542,9 @@ emitDeclImpl(decl, nullptr);
emitGLSLEntryPointFunc(context, func);
}
- else if(!isDefinition(func))
+ else
+#endif
+ if(!isDefinition(func))
{
// This is just a function declaration,
// and so we want to emit it as such.
@@ -5431,6 +5663,14 @@ emitDeclImpl(decl, nullptr);
emit("uniform ");
break;
+ case LayoutResourceKind::VertexInput:
+ emit("in ");
+ break;
+
+ case LayoutResourceKind::FragmentOutput:
+ emit("out ");
+ break;
+
default:
continue;
}
@@ -5520,7 +5760,16 @@ emitDeclImpl(decl, nullptr);
emitGLSLLayoutQualifier(*info);
}
- emit("uniform ");
+ if(type->As<GLSLShaderStorageBufferType>())
+ {
+ emit("layout(std430) buffer ");
+ }
+ // TODO: what to do with HLSL `tbuffer` style buffers?
+ else
+ {
+ emit("layout(std140) uniform ");
+ }
+
emit(getIRName(varDecl));
emit("\n{\n");
@@ -5852,7 +6101,7 @@ emitDeclImpl(decl, nullptr);
EmitContext* context,
IRModule* module)
{
- for (auto gv : module->globalValues)
+ for( auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
{
emitIRUsedTypesForValue(context, gv);
}
@@ -5864,7 +6113,7 @@ emitDeclImpl(decl, nullptr);
{
emitIRUsedTypesForModule(context, module);
- for (auto gv : module->globalValues)
+ for( auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
{
emitIRGlobalInst(context, gv);
}
@@ -5906,6 +6155,7 @@ String emitEntryPoint(
CodeGenTarget target)
{
auto translationUnit = entryPoint->getTranslationUnit();
+ auto session = entryPoint->compileRequest->mSession;
SharedEmitContext sharedContext;
sharedContext.target = target;
@@ -6007,6 +6257,14 @@ String emitEntryPoint(
// need to link in (and then inline) target-specific implementations
// for the library functions that the user called.
+ switch(target)
+ {
+ case CodeGenTarget::GLSL:
+ legalizeEntryPointsForGLSL(session, lowered);
+ break;
+ }
+
+
// TODO: do we want to emit directly from IR, or translate the
// IR back into AST for emission?
diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h
index 3a7bbb503..6f6af13cb 100644
--- a/source/slang/ir-insts.h
+++ b/source/slang/ir-insts.h
@@ -11,6 +11,7 @@
#include "compiler.h"
#include "ir.h"
#include "syntax.h"
+#include "type-layout.h"
namespace Slang {
@@ -40,6 +41,7 @@ struct IREntryPointDecoration : IRDecoration
enum { kDecorationOp = kIRDecorationOp_EntryPoint };
Profile profile;
+ EntryPointLayout* layout;
};
// Associates a compute-shader entry point function
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index ee28227e3..a79dc3992 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -600,26 +600,172 @@ namespace Slang
IRModule* IRBuilder::createModule()
{
- return new IRModule();
+ auto module = new IRModule();
+ module->session = getSession();
+ return module;
}
+ void IRGlobalValue::insertBefore(IRGlobalValue* other)
+ {
+ assert(other);
+ insertBefore(other, other->parentModule);
+ }
+
+ void IRGlobalValue::insertBefore(IRGlobalValue* other, IRModule* module)
+ {
+ assert(other || module);
+
+ if(!other) other = module->firstGlobalValue;
+ if(!module) module = other->parentModule;
+
+ assert(module);
+
+ auto nn = other;
+ auto pp = other ? other->prevGlobalValue : nullptr;
+
+ if(pp)
+ {
+ pp->nextGlobalValue = this;
+ }
+ else
+ {
+ module->firstGlobalValue = this;
+ }
+
+ if(nn)
+ {
+ nn->prevGlobalValue = this;
+ }
+ else
+ {
+ module->lastGlobalValue = this;
+ }
+
+ this->prevGlobalValue = pp;
+ this->nextGlobalValue = nn;
+ this->parentModule = module;
+ }
+
+ void IRGlobalValue::insertAtStart(IRModule* module)
+ {
+ insertBefore(module->firstGlobalValue, module);
+ }
+
+ void IRGlobalValue::insertAfter(IRGlobalValue* other)
+ {
+ assert(other);
+ insertAfter(other, other->parentModule);
+ }
+
+ void IRGlobalValue::insertAfter(IRGlobalValue* other, IRModule* module)
+ {
+ assert(other || module);
+
+ if(!other) other = module->lastGlobalValue;
+ if(!module) module = other->parentModule;
+
+ assert(module);
+
+ auto pp = other;
+ auto nn = other ? other->nextGlobalValue : nullptr;
+
+ if(pp)
+ {
+ pp->nextGlobalValue = this;
+ }
+ else
+ {
+ module->firstGlobalValue = this;
+ }
+
+ if(nn)
+ {
+ nn->prevGlobalValue = this;
+ }
+ else
+ {
+ module->lastGlobalValue = this;
+ }
+
+ this->prevGlobalValue = pp;
+ this->nextGlobalValue = nn;
+ this->parentModule = module;
+ }
+
+ void IRGlobalValue::insertAtEnd(IRModule* module)
+ {
+ assert(module);
+ insertAfter(module->lastGlobalValue, module);
+ }
+
+ void IRGlobalValue::removeFromParent()
+ {
+ auto module = parentModule;
+ if(!module)
+ return;
+
+ auto pp = this->prevGlobalValue;
+ auto nn = this->nextGlobalValue;
+
+ if(pp)
+ {
+ pp->nextGlobalValue = nn;
+ }
+ else
+ {
+ module->firstGlobalValue = nn;
+ }
+
+ if( nn )
+ {
+ nn->prevGlobalValue = pp;
+ }
+ else
+ {
+ module->lastGlobalValue = pp;
+ }
+ }
+
+ void IRGlobalValue::moveToEnd()
+ {
+ auto module = parentModule;
+ removeFromParent();
+ insertAtEnd(module);
+ }
+
+
+
+ void addGlobalValue(
+ IRModule* module,
+ IRGlobalValue* value)
+ {
+ if(!module)
+ return;
+
+ value->parentModule = module;
+ value->insertAfter(module->lastGlobalValue, module);
+ }
IRFunc* IRBuilder::createFunc()
{
- return createValue<IRFunc>(
+ IRFunc* func = createValue<IRFunc>(
this,
kIROp_Func,
nullptr);
+ addGlobalValue(getModule(), func);
+ return func;
}
IRGlobalVar* IRBuilder::createGlobalVar(
IRType* valueType)
{
auto ptrType = getSession()->getPtrType(valueType);
- return createValue<IRGlobalVar>(
+ IRGlobalVar* globalVar = createValue<IRGlobalVar>(
this,
kIROp_global_var,
ptrType);
+ addGlobalValue(getModule(), globalVar);
+ return globalVar;
}
IRBlock* IRBuilder::createBlock()
@@ -1646,7 +1792,7 @@ namespace Slang
IRDumpContext* context,
IRModule* module)
{
- for (auto gv : module->globalValues)
+ for( auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
{
dumpIRGlobalValue(context, gv);
}
@@ -1676,4 +1822,445 @@ namespace Slang
}
+ //
+ //
+ //
+
+ void IRValue::replaceUsesWith(IRValue* other)
+ {
+ // We will walk through the list of uses for the current
+ // instruction, and make them point to the other inst.
+ IRUse* ff = firstUse;
+
+ // No uses? Nothing to do.
+ if(!ff)
+ return;
+
+ IRUse* uu = ff;
+ for(;;)
+ {
+ // The uses had better all be uses of this
+ // instruction, or invariants are broken.
+ assert(uu->usedValue == this);
+
+ // Swap this use over to use the other value.
+ uu->usedValue = other;
+
+ // Try to move to the next use, but bail
+ // out if we are at the last one.
+ IRUse* next = uu->nextUse;
+ if( !next )
+ break;
+
+ uu = next;
+ }
+
+ // We are at the last use (and there must
+ // be at least one, because we handled
+ // the case of an empty list earlier).
+ assert(uu);
+
+ // Our job at this point is to splice
+ // our list of uses onto the other
+ // value's uses.
+ //
+ // If the value already had uses, then
+ // we need to patch our new list onto
+ // the front.
+ if( auto nn = other->firstUse )
+ {
+ uu->nextUse = nn;
+ nn->prevLink = &uu->nextUse;
+ }
+
+ // No matter what, our list of
+ // uses will become the start
+ // of the list of uses for
+ // `other`
+ other->firstUse = ff;
+ ff->prevLink = &other->firstUse;
+
+ // And `this` will have no uses any more.
+ this->firstUse = nullptr;
+ }
+
+ void IRValue::deallocate()
+ {
+ // Run destructor to be sure...
+ this->~IRValue();
+
+ // And then free the memory
+ free((void*) this);
+ }
+
+ // Insert this instruction into the same basic block
+ // as `other`, right before it.
+ void IRInst::insertBefore(IRInst* other)
+ {
+ // Make sure this instruction has been removed from any previous parent
+ this->removeFromParent();
+
+ auto bb = other->parentBlock;
+ assert(bb);
+
+ auto pp = other->prevInst;
+ if( pp )
+ {
+ pp->nextInst = this;
+ }
+ else
+ {
+ bb->firstInst = this;
+ }
+
+ this->prevInst = pp;
+ this->nextInst = other;
+ this->parentBlock = bb;
+
+ other->prevInst = this;
+ }
+
+ // Remove this instruction from its parent block,
+ // and then destroy it (it had better have no uses!)
+ void IRInst::removeFromParent()
+ {
+ // If we don't currently have a parent, then
+ // we are doing fine.
+ if(!parentBlock)
+ return;
+
+ auto bb = parentBlock;
+ auto pp = prevInst;
+ auto nn = nextInst;
+
+ if(pp)
+ {
+ SLANG_ASSERT(pp->parentBlock == bb);
+ pp->nextInst = nn;
+ }
+ else
+ {
+ bb->firstInst = nn;
+ }
+
+ if(nn)
+ {
+ SLANG_ASSERT(nn->parentBlock == bb);
+ nn->prevInst = pp;
+ }
+ else
+ {
+ bb->lastInst = pp;
+ }
+
+ prevInst = nullptr;
+ nextInst = nullptr;
+ parentBlock = nullptr;
+ }
+
+ // Remove this instruction from its parent block,
+ // and then destroy it (it had better have no uses!)
+ void IRInst::removeAndDeallocate()
+ {
+ removeFromParent();
+ deallocate();
+ }
+
+
+ //
+ // Legalization of entry points for GLSL:
+ //
+
+ IRGlobalVar* addGlobalVariable(
+ IRModule* module,
+ Type* valueType)
+ {
+ auto session = module->session;
+
+ SharedIRBuilder shared;
+ shared.module = module;
+ shared.session = session;
+
+ IRBuilder builder;
+ builder.shared = &shared;
+
+ RefPtr<PtrType> ptrType = session->getPtrType(valueType);
+
+ return builder.createGlobalVar(valueType);
+ }
+
+ void moveValueBefore(
+ IRGlobalValue* valueToMove,
+ IRGlobalValue* placeBefore)
+ {
+ valueToMove->removeFromParent();
+ valueToMove->insertBefore(placeBefore);
+ }
+
+ void legalizeEntryPointForGLSL(
+ Session* session,
+ IRFunc* func,
+ IREntryPointDecoration* entryPointInfo)
+ {
+ auto module = func->parentModule;
+
+ auto entryPointLayout = entryPointInfo->layout;
+
+ // We require that the entry-point function has no uses,
+ // because otherwise we'd invalidate the signature
+ // at all existing call sites.
+ //
+ // TODO: the right thing to do here is to split any
+ // function that both gets called as an entry point
+ // and as an ordinary function.
+ assert(!func->firstUse);
+
+ // We create a dummy IR builder, since some of
+ // the functions require it.
+ //
+ // TODO: make some of these free functions...
+ //
+ SharedIRBuilder shared;
+ shared.module = module;
+ shared.session = session;
+ IRBuilder builder;
+ builder.shared = &shared;
+
+ // We will start by looking at the return type of the
+ // function, because that will enable us to do an
+ // early-out check to avoid more work.
+ //
+ // Specifically, we need to check if the function has
+ // a `void` return type, because there is no work
+ // to be done on its return value in that case.
+ auto resultType = func->getResultType();
+ if( resultType->Equals(session->getVoidType()) )
+ {
+ // In this case, the function doesn't return a value
+ // so we don't need to transform its `return` sites.
+ //
+ // We can also use this opportunity to quickly
+ // check if the function has any parameters, and if
+ // it doesn't use the chance to bail out immediately.
+ if( func->getParamCount() == 0 )
+ {
+ // This function is already legal for GLSL
+ // (at least in terms of parameter/result signature),
+ // so we won't bother doing anything at all.
+ return;
+ }
+
+ // If the function does have parameters, then we need
+ // to let the logic later in this function handle them.
+ }
+ else
+ {
+ // Function returns a value, so we need
+ // to introduce a new global variable
+ // to hold that value, and then replace
+ // any `returnVal` instructions with
+ // code to write to that variable.
+
+ auto resultVariable = addGlobalVariable(module, resultType);
+ moveValueBefore(resultVariable, func);
+
+ // We need to transfer layout information from the entry point
+ // down to the variable:
+ builder.addLayoutDecoration(resultVariable, entryPointLayout->resultLayout);
+
+ for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() )
+ {
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst )
+ {
+ if(ii->op != kIROp_ReturnVal)
+ continue;
+
+ IRReturnVal* returnInst = (IRReturnVal*) ii;
+ IRValue* resultValue = returnInst->getVal();
+
+
+
+ // `store <resultVariable> <resultValue>`
+ IRStore* storeInst = createInst<IRStore>(
+ &builder,
+ kIROp_Store,
+ nullptr,
+ resultVariable,
+ resultValue);
+
+ // `returnVoid`
+ IRReturnVoid* returnVoid = createInst<IRReturnVoid>(
+ &builder,
+ kIROp_ReturnVoid,
+ nullptr);
+
+ // Put the two new instructions before the old one
+ storeInst->insertBefore(returnInst);
+ returnVoid->insertBefore(returnInst);
+
+ // and then remove the old one.
+ returnInst->removeAndDeallocate();
+
+ // Make sure to resume our iteration at an
+ // appropriate instruciton, since we deleted
+ // the one we had been using.
+ ii = returnVoid;
+ }
+ }
+ }
+
+ // Next we will walk through any parameters of the entry-point function,
+ // and turn them into global variables.
+ if( auto firstBlock = func->getFirstBlock() )
+ {
+ IRInst* insertBeforeInst = firstBlock->getFirstInst();
+
+ UInt paramCounter = 0;
+ for( auto pp = firstBlock->getFirstParam(); pp; pp = pp->getNextParam() )
+ {
+ UInt paramIndex = paramCounter++;
+
+ // We assume that the entry-point layout includes information
+ // on each parameter, and that these arrays are kept aligned.
+ // Note that this means that any transformations that mess
+ // with function signatures will need to also update layout info...
+ //
+ assert(entryPointLayout->fields.Count() > paramIndex);
+ auto paramLayout = entryPointLayout->fields[paramIndex];
+
+ // We need to create a global variable that will replace the parameter.
+ // It seems superficially obvious that the variable should have
+ // the same type as the parameter.
+ // However, if the parameter was a pointer, in order to
+ // support `out` or `in out` parameter passing, we need
+ // to be sure to allocate a variable of the pointed-to
+ // type instead.
+ //
+ // We also need to replace uses of the parameter with
+ // uses of the variable, and the exact logic there
+ // will differ a bit between the pointer and non-pointer
+ // cases.
+ auto paramType = pp->getType();
+
+ // TODO: We need to distinguish any true pointers in the
+ // user's code from pointers that only exist for
+ // parameter-passing. This `PtrType` here should actually
+ // be `OutTypeBase`, but I'm not confident that all
+ // the other code is handling that correctly...
+ if(auto paramPtrType = paramType->As<PtrType>() )
+ {
+ // Okay, we have the more interesting case here,
+ // where the parameter was being passed by reference.
+ // This actually makes our life pretty easy, though,
+ // since we can simply replace any uses of the existing
+ // pointer with the global variable (since it will
+ // be a pointer to storage).
+
+ // We start by creating the global variable, using
+ // the pointed-to type:
+ auto valueType = paramPtrType->getValueType();
+ auto paramVariable = addGlobalVariable(module, paramType);
+ moveValueBefore(paramVariable, func);
+
+ // TODO: We need to special-case `in out` variables here,
+ // because they actually need to be lowered to *two*
+ // global variables, not just one. We then need
+ // to emit logic to initialize the output variable
+ // based on the input at the start of the entry point,
+ // and then use the output variable thereafter.
+ //
+ // TODO: Actually, I need to double-check that it is
+ // legal in GLSL to use shader input/output parameters
+ // as temporaries in general; if not then we'd need
+ // to introduce a temporary no matter what.
+
+ // Next we attach the layout information from the
+ // original parameter to the new global variable,
+ // so that we can lay it out correctly when generating
+ // target code:
+ builder.addLayoutDecoration(paramVariable, paramLayout);
+
+ // And finally, we go ahead and replace all the
+ // uses of the parameter (which was a pointer) with
+ // uses of the new global variable's address.
+ pp->replaceUsesWith(paramVariable);
+ }
+ else
+ {
+ // This is the "easy" case where the parameter wasn't
+ // being passed by reference. We start by just creating
+ // a variable of the appropriate type, and attaching
+ // the required layout information to it.
+ auto paramVariable = addGlobalVariable(module, paramType);
+ moveValueBefore(paramVariable, func);
+ builder.addLayoutDecoration(paramVariable, paramLayout);
+
+ // Next we need to replace uses of the parameter with
+ // references to the variable. We are going to do that
+ // somewhat naively, by simply loading the variable
+ // at the start.
+
+ IRInst* loadInst = builder.emitLoad(paramVariable);
+ loadInst->insertBefore(insertBeforeInst);
+
+ pp->replaceUsesWith(loadInst);
+ }
+ }
+
+ // At this point we should have eliminated all uses of the
+ // parameters of the entry block. Also, our control-flow
+ // rules mean that the entry block cannot be the target
+ // of any branches in the code, so there can't be
+ // any control-flow ops that try to match the parameter
+ // list.
+ //
+ // We can safely go through and destroy the parameters
+ // themselves, and then clear out the parameter list.
+ for( auto pp = firstBlock->getFirstParam(); pp; )
+ {
+ auto next = pp->getNextParam();
+ pp->deallocate();
+ pp = next;
+ }
+ firstBlock->firstParam = nullptr;
+ firstBlock->lastParam = nullptr;
+ }
+
+ // Finally, we need to patch up the type of the entry point,
+ // because it is no longer accurate.
+
+ auto voidFuncType = new FuncType();
+ voidFuncType->resultType = session->getVoidType();
+ func->type = voidFuncType;
+
+ // TODO: we should technically be constructing
+ // a new `EntryPointLayout` here to reflect
+ // the way that things have been moved around.
+ }
+
+ void legalizeEntryPointsForGLSL(
+ Session* session,
+ IRModule* module)
+ {
+ // We need to walk through all the global entry point
+ // declarations, and transform them to comply with
+ // GLSL rules.
+ for( auto globalValue = module->getFirstGlobalValue(); globalValue; globalValue = globalValue->getNextValue())
+ {
+ // Is the global value a function?
+ if(globalValue->op != kIROp_Func)
+ continue;
+ IRFunc* func = (IRFunc*) globalValue;
+
+ // Is the function an entry point?
+ IREntryPointDecoration* entryPointDecoration = func->findDecoration<IREntryPointDecoration>();
+ if(!entryPointDecoration)
+ continue;
+
+ // Okay, we need to legalize this one.
+ legalizeEntryPointForGLSL(session, func, entryPointDecoration);
+ }
+ }
+
+
}
diff --git a/source/slang/ir.h b/source/slang/ir.h
index 82684ae17..4fd165c33 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -15,6 +15,7 @@ class Decl;
class FuncType;
class Layout;
class Type;
+class Session;
struct IRFunc;
struct IRInst;
@@ -133,7 +134,7 @@ struct IRValue
Type* getType() { return type; }
// The linked list of decorations attached to this value
- IRDecoration* firstDecoration;
+ IRDecoration* firstDecoration = nullptr;
// Look up a decoration in the list of decorations
IRDecoration* findDecorationImpl(IRDecorationOp op);
@@ -144,9 +145,16 @@ struct IRValue
}
// The first use of this value (start of a linked list)
- IRUse* firstUse;
+ IRUse* firstUse = nullptr;
+ // Replace all uses of this value with `other`, so
+ // that this value will now have no uses.
+ void replaceUsesWith(IRValue* other);
+
+ // Free a value (which needs to have been removed
+ // from its parent, had its uses eliminated, etc.)
+ void deallocate();
};
// Instructions are values that can be executed,
@@ -180,6 +188,18 @@ struct IRInst : IRValue
{
return getArgs()[index].usedValue;
}
+
+ // Insert this instruction into the same basic block
+ // as `other`, right before it.
+ void insertBefore(IRInst* other);
+
+ // Remove this instruction from its parent block,
+ // but don't delete it, or replace uses.
+ void removeFromParent();
+
+ // Remove this instruction from its parent block,
+ // and then destroy it (it had better have no uses!)
+ void removeAndDeallocate();
};
typedef int64_t IRIntegerValue;
@@ -268,7 +288,27 @@ struct IRBlock : IRValue
typedef FuncType IRFuncType;
struct IRGlobalValue : IRValue
-{};
+{
+ IRModule* parentModule;
+
+ IRGlobalValue* nextGlobalValue;
+ IRGlobalValue* prevGlobalValue;
+
+ IRGlobalValue* getNextValue() { return nextGlobalValue; }
+ IRGlobalValue* getPrevValue() { return prevGlobalValue; }
+
+ void insertBefore(IRGlobalValue* other);
+ void insertBefore(IRGlobalValue* other, IRModule* module);
+ void insertAtStart(IRModule* module);
+
+ void insertAfter(IRGlobalValue* other);
+ void insertAfter(IRGlobalValue* other, IRModule* module);
+ void insertAtEnd(IRModule* module);
+
+ void removeFromParent();
+
+ void moveToEnd();
+};
// A function is a parent to zero or more blocks of instructions.
//
@@ -311,14 +351,16 @@ struct IRFunc : IRGlobalValue
// A module is a parent to functions, global variables, types, etc.
struct IRModule : RefObject
{
- // The designated entry-point function, if any
- IRFunc* entryPoint;
+ // The compilation session in use.
+ Session* session;
// A list of all the functions and other
// global values declared in this module.
- List<IRGlobalValue*> globalValues;
+ IRGlobalValue* firstGlobalValue = nullptr;
+ IRGlobalValue* lastGlobalValue = nullptr;
- // TODO: need a symbol of all the global variables too
+ IRGlobalValue* getFirstGlobalValue() { return firstGlobalValue; }
+ IRGlobalValue* getlastGlobalValue() { return lastGlobalValue; }
};
void printSlangIRAssembly(StringBuilder& builder, IRModule* module);
@@ -326,6 +368,13 @@ String getSlangIRAssembly(IRModule* module);
void dumpIR(IRModule* module);
+// IR transformations
+
+// Transform shader entry points so that they conform to GLSL rules.
+void legalizeEntryPointsForGLSL(
+ Session* session,
+ IRModule* module);
+
}
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index 502d63320..b9307b007 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -2117,8 +2117,6 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// TODO: need to handle global with initializer!
}
- getBuilder()->getModule()->globalValues.Add(irGlobal);
-
return globalVal;
}
@@ -2837,7 +2835,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
getBuilder()->addHighLevelDeclDecoration(irFunc, decl);
- getBuilder()->getModule()->globalValues.Add(irFunc);
+ // For convenience, ensure that any additional global
+ // values that were emitted while outputting the function
+ // body appear before the function itself in the list
+ // of global values.
+ irFunc->moveToEnd();
return LoweredValInfo::simple(irFunc);
}
@@ -2939,6 +2941,7 @@ static void lowerEntryPointToIR(
auto entryPointDecoration = builder->addDecoration<IREntryPointDecoration>(irFunc);
entryPointDecoration->profile = profile;
+ entryPointDecoration->layout = entryPointLayout;
// Attach layout information here.
builder->addLayoutDecoration(irFunc, entryPointLayout);