// Code for LLVM::GEPOp.
//===----------------------------------------------------------------------===//
+/// Populates `indices` with positions of GEP indices that would correspond to
+/// LLVMStructTypes potentially nested in the given type. The type currently
+/// visited gets `currentIndex` and LLVM container types are visited
+/// recursively. The recursion is bounded and takes care of recursive types by
+/// means of the `visited` set.
+static void recordStructIndices(Type type, unsigned currentIndex,
+ SmallVectorImpl<unsigned> &indices,
+ SmallVectorImpl<unsigned> *structSizes,
+ SmallPtrSet<Type, 4> &visited) {
+ if (visited.contains(type))
+ return;
+
+ visited.insert(type);
+
+ llvm::TypeSwitch<Type>(type)
+ .Case<LLVMStructType>([&](LLVMStructType structType) {
+ indices.push_back(currentIndex);
+ if (structSizes)
+ structSizes->push_back(structType.getBody().size());
+ for (Type elementType : structType.getBody())
+ recordStructIndices(elementType, currentIndex + 1, indices,
+ structSizes, visited);
+ })
+ .Case<VectorType, LLVMScalableVectorType, LLVMFixedVectorType,
+ LLVMArrayType>([&](auto containerType) {
+ recordStructIndices(containerType.getElementType(), currentIndex + 1,
+ indices, structSizes, visited);
+ });
+}
+
+/// Populates `indices` with positions of GEP indices that correspond to
+/// LLVMStructTypes potentially nested in the given `baseGEPType`, which must
+/// be either an LLVMPointer type or a vector thereof. If `structSizes` is
+/// provided, it is populated with sizes of the indexed structs for bounds
+/// verification purposes.
+static void
+findKnownStructIndices(Type baseGEPType, SmallVectorImpl<unsigned> &indices,
+ SmallVectorImpl<unsigned> *structSizes = nullptr) {
+ Type type = baseGEPType;
+ if (auto vectorType = type.dyn_cast<VectorType>())
+ type = vectorType.getElementType();
+ if (auto scalableVectorType = type.dyn_cast<LLVMScalableVectorType>())
+ type = scalableVectorType.getElementType();
+ if (auto fixedVectorType = type.dyn_cast<LLVMFixedVectorType>())
+ type = fixedVectorType.getElementType();
+
+ Type pointeeType = type.cast<LLVMPointerType>().getElementType();
+ SmallPtrSet<Type, 4> visited;
+ recordStructIndices(pointeeType, /*currentIndex=*/1, indices, structSizes,
+ visited);
+}
+
void GEPOp::build(OpBuilder &builder, OperationState &result, Type resultType,
Value basePtr, ValueRange operands,
ArrayRef<NamedAttribute> attributes) {
Value basePtr, ValueRange indices,
ArrayRef<int32_t> structIndices,
ArrayRef<NamedAttribute> attributes) {
+ SmallVector<Value> remainingIndices;
+ SmallVector<int32_t> updatedStructIndices(structIndices.begin(),
+ structIndices.end());
+ SmallVector<unsigned> structRelatedPositions;
+ findKnownStructIndices(basePtr.getType(), structRelatedPositions);
+
+ SmallVector<unsigned> operandsToErase;
+ for (unsigned pos : structRelatedPositions) {
+ // GEP may not be indexing as deep as some structs are located.
+ if (pos >= structIndices.size())
+ continue;
+
+ // If the index is already static, it's fine.
+ if (structIndices[pos] != kDynamicIndex)
+ continue;
+
+ // Find the corresponding operand.
+ unsigned operandPos =
+ std::count(structIndices.begin(), std::next(structIndices.begin(), pos),
+ kDynamicIndex);
+
+ // Extract the constant value from the operand and put it into the attribute
+ // instead.
+ APInt staticIndexValue;
+ bool matched =
+ matchPattern(indices[operandPos], m_ConstantInt(&staticIndexValue));
+ (void)matched;
+ assert(matched && "index into a struct must be a constant");
+ assert(staticIndexValue.sge(APInt::getSignedMinValue(/*numBits=*/32)) &&
+ "struct index underflows 32-bit integer");
+ assert(staticIndexValue.sle(APInt::getSignedMaxValue(/*numBits=*/32)) &&
+ "struct index overflows 32-bit integer");
+ auto staticIndex = static_cast<int32_t>(staticIndexValue.getSExtValue());
+ updatedStructIndices[pos] = staticIndex;
+ operandsToErase.push_back(operandPos);
+ }
+
+ for (unsigned i = 0, e = indices.size(); i < e; ++i) {
+ if (llvm::find(operandsToErase, i) == operandsToErase.end())
+ remainingIndices.push_back(indices[i]);
+ }
+
+ assert(remainingIndices.size() == static_cast<size_t>(llvm::count(
+ updatedStructIndices, kDynamicIndex)) &&
+ "exected as many index operands as dynamic index attr elements");
+
result.addTypes(resultType);
result.addAttributes(attributes);
- result.addAttribute("structIndices", builder.getI32TensorAttr(structIndices));
+ result.addAttribute("structIndices",
+ builder.getI32TensorAttr(updatedStructIndices));
result.addOperands(basePtr);
- result.addOperands(indices);
+ result.addOperands(remainingIndices);
}
static ParseResult
});
}
+LogicalResult verify(LLVM::GEPOp gepOp) {
+ SmallVector<unsigned> indices;
+ SmallVector<unsigned> structSizes;
+ findKnownStructIndices(gepOp.getBase().getType(), indices, &structSizes);
+ for (unsigned i = 0, e = indices.size(); i < e; ++i) {
+ unsigned index = indices[i];
+ // GEP may not be indexing as deep as some structs nested in the type.
+ if (index >= gepOp.getStructIndices().getNumElements())
+ continue;
+
+ int32_t staticIndex = gepOp.getStructIndices().getValues<int32_t>()[index];
+ if (staticIndex == LLVM::GEPOp::kDynamicIndex)
+ return gepOp.emitOpError() << "expected index " << index
+ << " indexing a struct to be constant";
+ if (staticIndex < 0 || static_cast<unsigned>(staticIndex) >= structSizes[i])
+ return gepOp.emitOpError()
+ << "index " << index << " indexing a struct is out of bounds";
+ }
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// Builder, printer and parser for for LLVM::LoadOp.
//===----------------------------------------------------------------------===//