Skip to content

[NFC][SYCL] Refactor SpecConstantsPass #10613

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

Merged
merged 3 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all 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

This file was deleted.

200 changes: 114 additions & 86 deletions llvm/tools/sycl-post-link/SpecConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,115 @@ Instruction *emitSpecConstantRecursive(Type *Ty, Instruction *InsertBefore,
DefaultValue);
}

/// Function creates load instruction from the given Buffer by the given Offset.
/// Function returns the value of load instruction.
Value *createLoadFromBuffer(CallInst *InsertBefore, Value *Buffer,
size_t Offset, Type *SCType) {
LLVMContext &C = InsertBefore->getContext();
Type *Int8Ty = Type::getInt8Ty(C);
Type *Int32Ty = Type::getInt32Ty(C);
GetElementPtrInst *GEP = GetElementPtrInst::Create(
Int8Ty, Buffer, {ConstantInt::get(Int32Ty, Offset, false)}, "gep",
InsertBefore);

Instruction *BitCast = nullptr;
if (SCType->isIntegerTy(1)) // No bitcast to i1 before load
BitCast = GEP;
else
BitCast =
new BitCastInst(GEP, PointerType::get(SCType, GEP->getAddressSpace()),
"bc", InsertBefore);

// When we encounter i1 spec constant, we still load the whole byte
Value *Load = new LoadInst(SCType->isIntegerTy(1) ? Int8Ty : SCType, BitCast,
"load", InsertBefore);
if (SCType->isIntegerTy(1)) // trunc back to i1 if necessary
Load = CastInst::CreateIntegerCast(Load, SCType, /* IsSigned */ false,
"tobool", InsertBefore);

return Load;
}

/// Function tries to dig out the initializer from the given CallInst to
/// SpecConst function. ArgIndex is the expected index of the function operand
/// leading to the initializer.
///
/// Examples:
/// 1)
/// %"spec_id" = type { i32 }
/// @value = internal addrspace(1) constant %"spec_id" { i32 123 }, align 4
/// call spir_func i32 @sycl_getScalar2020SpecConst(%1, @value, %2)
///
/// 2)
/// %"spec_id" = type { %A }
/// %A = type { i32 }
/// @value = constant %"spec_id" { %A { i32 1 } }, align 4
/// call spir_func void @getCompositeSpecConst(%1, %2, @value, %3)
Constant *getSpecConstInitializerFromCI(CallInst *CI, unsigned ArgIndex) {
auto *GV =
cast<GlobalVariable>(CI->getArgOperand(ArgIndex)->stripPointerCasts());

// Go through global variable if the argument was not null.
assert(GV->hasInitializer() && "GV is expected to have initializer");
Constant *Initializer = GV->getInitializer();
assert((isa<ConstantAggregate>(Initializer) || Initializer->isZeroValue()) &&
"expected specialization_id instance");
// specialization_id structure contains a single field which is the
// default value of corresponding specialization constant.
return Initializer->getAggregateElement(0u);
}

/// Function replaces last Metadata node in the given vector with new
/// node which contains given Padding.
void updatePaddingInLastMDNode(LLVMContext &Ctx,
MapVector<StringRef, MDNode *> &SCMetadata,
unsigned Padding) {
// The spec constant map can't be empty as the first offset is 0
// and so it can't be misaligned.
assert(!SCMetadata.empty() && "Cannot add padding to first spec constant");

// To communicate the padding to the runtime, update the metadata
// node of the previous spec constant to append a padding node. It
// can't be added in front of the current spec constant, as doing
// so would require the spec constant node to have a non-zero
// CompositeOffset which breaks accessing it in the runtime.
auto Last = SCMetadata.back();

// Emulated spec constants don't use composite so should
// always be formatted as (SymID, ID, Offset, Size), except when
// they include padding, but since padding is added at insertion
// of the next element, the last element of the map can never be
// padded.
assert(Last.second->getNumOperands() == 4 &&
"Incorrect emulated spec constant format");

Type *Int32Ty = Type::getInt32Ty(Ctx);
SmallVector<Metadata *, 16> MDOps;

// Copy the existing metadata.
MDOps.push_back(Last.second->getOperand(0));
MDOps.push_back(Last.second->getOperand(1));
MDOps.push_back(Last.second->getOperand(2));
auto &SizeOp = Last.second->getOperand(3);
MDOps.push_back(SizeOp);

// Extract the size of the previous node to use as CompositeOffset
// for the padding node.
auto PrevSize = mdconst::extract<ConstantInt>(SizeOp)->getValue();

// The max value is a magic value used for padding that the
// runtime knows to skip.
MDOps.push_back(ConstantAsMetadata::get(Constant::getIntegerValue(
Int32Ty, APInt(32, std::numeric_limits<unsigned>::max()))));
MDOps.push_back(
ConstantAsMetadata::get(Constant::getIntegerValue(Int32Ty, PrevSize)));
MDOps.push_back(ConstantAsMetadata::get(
Constant::getIntegerValue(Int32Ty, APInt(32, Padding))));

// Replace the last metadata node with the node including the padding.
SCMetadata[Last.first] = MDNode::get(Ctx, MDOps);
}

} // namespace

PreservedAnalyses SpecConstantsPass::run(Module &M,
Expand All @@ -627,6 +736,7 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
// setting (see below).
bool IRModified = false;

LLVMContext &Ctx = M.getContext();
for (Function &F : M) {
if (!F.isDeclaration())
continue;
Expand Down Expand Up @@ -665,23 +775,7 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
StringRef SymID = getStringLiteralArg(CI, NameArgNo, DelInsts);
Value *Replacement = nullptr;

Constant *DefaultValue = nullptr;
// There is a mechanism to specify the default value in SYCL 2020.
// It is stored as an initializer of a global variable referenced by
// the second argument of the intrinsic.
auto *GV = dyn_cast<GlobalVariable>(
CI->getArgOperand(NameArgNo + 1)->stripPointerCasts());
// Go through global variable if the second argument was not null.
if (GV) {
assert(GV->hasInitializer() && "expected initializer");
auto *Initializer = GV->getInitializer();
assert((isa<ConstantAggregate>(Initializer) ||
Initializer->isZeroValue()) &&
"expected specialization_id instance");
// specialization_id structure contains a single field which is the
// default value of corresponding specialization constant.
DefaultValue = Initializer->getAggregateElement(0u);
}
Constant *DefaultValue = getSpecConstInitializerFromCI(CI, NameArgNo + 1);

bool IsNewSpecConstant = false;
unsigned Padding = 0;
Expand Down Expand Up @@ -743,54 +837,7 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
OffsetMap[SymID] = NextOffset;

assert(CurrentOffset % Align == 0 && "Alignment calculation error");

// The spec constant map can't be empty as the first offset is 0
// and so it can't be misaligned.
assert(!SCMetadata.empty() &&
"Cannot add padding to first spec constant");

// To communicate the padding to the runtime, update the metadata
// node of the previous spec constant to append a padding node. It
// can't be added in front of the current spec constant, as doing
// so would require the spec constant node to have a non-zero
// CompositeOffset which breaks accessing it in the runtime.
auto Prev = SCMetadata.back();

// Emulated spec constants don't use composite so should
// always be formatted as (SymID, ID, Offset, Size), except when
// they include padding, but since padding is added at insertion
// of the next element, the last element of the map can never be
// padded.
assert(Prev.second->getNumOperands() == 4 &&
"Incorrect emulated spec constant format");

LLVMContext &Ctx = M.getContext();
auto *Int32Ty = Type::getInt32Ty(Ctx);
SmallVector<Metadata *, 16> MDOps;

// Copy the existing metadata.
MDOps.push_back(Prev.second->getOperand(0));
MDOps.push_back(Prev.second->getOperand(1));
MDOps.push_back(Prev.second->getOperand(2));
auto &SizeOp = Prev.second->getOperand(3);
MDOps.push_back(SizeOp);

// Extract the size of the previous node to use as CompositeOffset
// for the padding node.
auto PrevSize = mdconst::extract<ConstantInt>(SizeOp)->getValue();

// The max value is a magic value used for padding that the
// runtime knows to skip.
MDOps.push_back(ConstantAsMetadata::get(Constant::getIntegerValue(
Int32Ty, APInt(32, std::numeric_limits<unsigned>::max()))));
MDOps.push_back(ConstantAsMetadata::get(
Constant::getIntegerValue(Int32Ty, PrevSize)));
MDOps.push_back(ConstantAsMetadata::get(
Constant::getIntegerValue(Int32Ty, APInt(32, Padding))));

// Replace previous metadata node with the node including the
// padding.
SCMetadata[Prev.first] = MDNode::get(Ctx, MDOps);
updatePaddingInLastMDNode(Ctx, SCMetadata, Padding);
}

SCMetadata[SymID] = generateSpecConstantMetadata(
Expand All @@ -800,31 +847,12 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
NextOffset += Size;
}

Type *Int8Ty = Type::getInt8Ty(CI->getContext());
Type *Int32Ty = Type::getInt32Ty(CI->getContext());
GetElementPtrInst *GEP = GetElementPtrInst::Create(
Int8Ty, RTBuffer, {ConstantInt::get(Int32Ty, CurrentOffset, false)},
"gep", CI);

Instruction *BitCast = nullptr;
if (SCTy->isIntegerTy(1)) // No bitcast to i1 before load
BitCast = GEP;
else
BitCast = new BitCastInst(
GEP, PointerType::get(SCTy, GEP->getAddressSpace()), "bc", CI);

// When we encounter i1 spec constant, we still load the whole byte
Replacement = new LoadInst(SCTy->isIntegerTy(1) ? Int8Ty : SCTy,
BitCast, "load", CI);
if (SCTy->isIntegerTy(1)) // trunc back to i1 if necessary
Replacement = CastInst::CreateIntegerCast(
Replacement, SCTy, /* IsSigned */ false, "tobool", CI);
Replacement = createLoadFromBuffer(CI, RTBuffer, CurrentOffset, SCTy);
}

if (IsNewSpecConstant && DefaultValue) {
if (IsNewSpecConstant) {
if (Padding != 0) {
// Initialize the padding with null data
LLVMContext &Ctx = DefaultValue->getContext();
auto PadTy = ArrayType::get(Type::getInt8Ty(Ctx), Padding);
DefaultsMetadata.push_back(MDNode::get(
Ctx,
Expand Down