Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix nullptr in generic specialization #6066

Merged
merged 5 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions source/slang/slang-ir-clone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ IRInst* cloneInstAndOperands(IRCloneEnv* env, IRBuilder* builder, IRInst* oldIns
SLANG_ASSERT(builder);
SLANG_ASSERT(oldInst);

// This logic will not handle any instructions
// with special-case data attached, but that only
// applies to `IRConstant`s at this point, and those
// should only appear at the global scope rather than
// in function bodies.
//
// TODO: It would be easy enough to extend this logic
// to handle constants gracefully, if it ever comes up.
//
SLANG_ASSERT(!as<IRConstant>(oldInst));
// IRConstants contain data other than just the operands, so cloning them
// is done separately.
IRConstant* oldConstant = as<IRConstant>(oldInst);
if (oldConstant)
{
IRConstant* newConstant = builder->getClonedConstantValue(*oldConstant);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always create a new inst without going through the deduplication logic. If we already have an inst that represents the cloned inst, this will break the invariant that all constants in global scope are deduplicated.

Given that the case will only ever arise for pointer literals because their types may be generic, we should handle just the PtrLit case and leave other opcodes as is, and then call emitPtrLit instead of doing all these cloning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, sounds good. This would also keep the PR much smaller & cleaner since getClonedConstantValue would no longer need to separated from _findOrEmitConstant.

builder->addInst(newConstant);
newConstant->sourceLoc = oldInst->sourceLoc;
return newConstant;
}

// We start by mapping the type of the orignal instruction
// to its replacement value, if any.
Expand Down
1 change: 1 addition & 0 deletions source/slang/slang-ir-insts.h
Original file line number Diff line number Diff line change
Expand Up @@ -3601,6 +3601,7 @@ struct IRBuilder
IRPtrLit* getNullVoidPtrValue() { return getNullPtrValue(getPtrType(getVoidType())); }
IRVoidLit* getVoidValue();
IRInst* getCapabilityValue(CapabilitySet const& caps);
IRConstant* getClonedConstantValue(IRConstant& value);

IRBasicType* getBasicType(BaseType baseType);
IRBasicType* getVoidType();
Expand Down
9 changes: 9 additions & 0 deletions source/slang/slang-ir-link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,15 @@ IRInst* cloneInst(
{
// We need to special-case any instruction that is not
// allocated like an ordinary `IRInst` with trailing args.
case kIROp_IntLit:
case kIROp_FloatLit:
case kIROp_BoolLit:
case kIROp_StringLit:
case kIROp_BlobLit:
case kIROp_PtrLit:
case kIROp_VoidLit:
return cloneValue(context, originalInst);

case kIROp_Func:
return cloneFuncImpl(context, builder, cast<IRFunc>(originalInst), originalValues);

Expand Down
139 changes: 74 additions & 65 deletions source/slang/slang-ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2192,71 +2192,7 @@ IRConstant* IRBuilder::_findOrEmitConstant(IRConstant& keyInst)
return irValue;
}

// Calculate the minimum object size (ie not including the payload of value)
const size_t prefixSize = SLANG_OFFSET_OF(IRConstant, value);

switch (keyInst.getOp())
{
default:
SLANG_UNEXPECTED("missing case for IR constant");
break;

case kIROp_BoolLit:
case kIROp_IntLit:
{
const size_t instSize = prefixSize + sizeof(IRIntegerValue);
irValue = static_cast<IRConstant*>(
_createInst(instSize, keyInst.getFullType(), keyInst.getOp()));
irValue->value.intVal = keyInst.value.intVal;
break;
}
case kIROp_FloatLit:
{
const size_t instSize = prefixSize + sizeof(IRFloatingPointValue);
irValue = static_cast<IRConstant*>(
_createInst(instSize, keyInst.getFullType(), keyInst.getOp()));
irValue->value.floatVal = keyInst.value.floatVal;
break;
}
case kIROp_PtrLit:
{
const size_t instSize = prefixSize + sizeof(void*);
irValue = static_cast<IRConstant*>(
_createInst(instSize, keyInst.getFullType(), keyInst.getOp()));
irValue->value.ptrVal = keyInst.value.ptrVal;
break;
}
case kIROp_VoidLit:
{
const size_t instSize = prefixSize + sizeof(void*);
irValue = static_cast<IRConstant*>(
_createInst(instSize, keyInst.getFullType(), keyInst.getOp()));
irValue->value.ptrVal = keyInst.value.ptrVal;
break;
}
case kIROp_BlobLit:
case kIROp_StringLit:
{
const UnownedStringSlice slice = keyInst.getStringSlice();

const size_t sliceSize = slice.getLength();
const size_t instSize =
prefixSize + offsetof(IRConstant::StringValue, chars) + sliceSize;

irValue = static_cast<IRConstant*>(
_createInst(instSize, keyInst.getFullType(), keyInst.getOp()));

IRConstant::StringValue& dstString = irValue->value.stringVal;

dstString.numChars = uint32_t(sliceSize);
// Turn into pointer to avoid warning of array overrun
char* dstChars = dstString.chars;
// Copy the chars
memcpy(dstChars, slice.begin(), sliceSize);

break;
}
}
irValue = getClonedConstantValue(keyInst);

key.inst = irValue;
m_dedupContext->getConstantMap().add(key, irValue);
Expand Down Expand Up @@ -2469,6 +2405,79 @@ IRInst* IRBuilder::getCapabilityValue(CapabilitySet const& caps)
conjunctions.getBuffer());
}

IRConstant* IRBuilder::getClonedConstantValue(IRConstant& value)
{
IRConstant* irValue = nullptr;

// Calculate the minimum object size (ie not including the payload of value)
const size_t prefixSize = SLANG_OFFSET_OF(IRConstant, value);

switch (value.getOp())
{
default:
SLANG_UNEXPECTED("missing case for IR constant");
break;

case kIROp_BoolLit:
case kIROp_IntLit:
{
const size_t instSize = prefixSize + sizeof(IRIntegerValue);
irValue =
static_cast<IRConstant*>(_createInst(instSize, value.getFullType(), value.getOp()));
irValue->value.intVal = value.value.intVal;
break;
}
case kIROp_FloatLit:
{
const size_t instSize = prefixSize + sizeof(IRFloatingPointValue);
irValue =
static_cast<IRConstant*>(_createInst(instSize, value.getFullType(), value.getOp()));
irValue->value.floatVal = value.value.floatVal;
break;
}
case kIROp_PtrLit:
{
const size_t instSize = prefixSize + sizeof(void*);
irValue =
static_cast<IRConstant*>(_createInst(instSize, value.getFullType(), value.getOp()));
irValue->value.ptrVal = value.value.ptrVal;
break;
}
case kIROp_VoidLit:
{
const size_t instSize = prefixSize + sizeof(void*);
irValue =
static_cast<IRConstant*>(_createInst(instSize, value.getFullType(), value.getOp()));
irValue->value.ptrVal = value.value.ptrVal;
break;
}
case kIROp_BlobLit:
case kIROp_StringLit:
{
const UnownedStringSlice slice = value.getStringSlice();

const size_t sliceSize = slice.getLength();
const size_t instSize =
prefixSize + offsetof(IRConstant::StringValue, chars) + sliceSize;

irValue =
static_cast<IRConstant*>(_createInst(instSize, value.getFullType(), value.getOp()));

IRConstant::StringValue& dstString = irValue->value.stringVal;

dstString.numChars = uint32_t(sliceSize);
// Turn into pointer to avoid warning of array overrun
char* dstChars = dstString.chars;
// Copy the chars
memcpy(dstChars, slice.begin(), sliceSize);

break;
}
}

return irValue;
}

static void canonicalizeInstOperands(IRBuilder& builder, IROp op, ArrayView<IRInst*> operands)
{
// For Array types, we always want to make sure its element count
Expand Down