summaryrefslogtreecommitdiff
path: root/source/slang/slang-ir-extract-value-from-type.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-ir-extract-value-from-type.cpp')
-rw-r--r--source/slang/slang-ir-extract-value-from-type.cpp278
1 files changed, 278 insertions, 0 deletions
diff --git a/source/slang/slang-ir-extract-value-from-type.cpp b/source/slang/slang-ir-extract-value-from-type.cpp
new file mode 100644
index 000000000..3019b5fd4
--- /dev/null
+++ b/source/slang/slang-ir-extract-value-from-type.cpp
@@ -0,0 +1,278 @@
+#include "slang-ir-extract-value-from-type.h"
+#include "slang-ir-layout.h"
+#include "slang-ir-insts.h"
+#define CHECK(x) SLANG_RELEASE_ASSERT((x) == SLANG_OK)
+
+namespace Slang
+{
+
+// Represents the result of finding the leaf-level value in a type that contains the
+// the entirety or the first half of the requested value at the specified offset.
+struct FindLeafValueResult
+{
+ IRInst* leafValue = nullptr; // The leaf-level value.
+ uint32_t valueSize = 0; // The size of the leaf-level value.
+ uint32_t offsetInValue = 0; // The offset in bytes within `leafValue` that contains the requested value.
+};
+
+FindLeafValueResult findLeafValueAtOffset(
+ TargetRequest* targetReq,
+ IRBuilder& builder,
+ IRType* dataType,
+ IRSizeAndAlignment& layout,
+ IRInst* src,
+ uint32_t offset)
+{
+ FindLeafValueResult result;
+ if (offset >= layout.size && offset < layout.getStride())
+ {
+ // We are extracting bits beyond the type size but within the stride boundary,
+ // return a 0 value in this case.
+ result.leafValue = builder.getIntValue(builder.getUIntType(), 0);
+ result.valueSize = 4;
+ result.offsetInValue = (uint32_t)(offset - layout.size);
+ return result;
+ }
+ switch (dataType->op)
+ {
+ case kIROp_StructType:
+ {
+ auto structType = as<IRStructType>(dataType);
+ for (auto field : structType->getFields())
+ {
+ IRIntegerValue fieldOffset = 0;
+ IRSizeAndAlignment fieldLayout;
+ CHECK(getNaturalSizeAndAlignment(targetReq, field->getFieldType(), &fieldLayout));
+ CHECK(getNaturalOffset(targetReq, field, &fieldOffset));
+ if (fieldOffset + fieldLayout.size > offset)
+ {
+ if (fieldOffset > offset)
+ {
+ // This field is starting after the requested offset,
+ // therefore the requested value is located at the "gap"
+ // between aligned fields, in this case the requested value
+ // is 0.
+ result.leafValue = builder.getIntValue(builder.getUIntType(), 0);
+ result.valueSize = 4;
+ result.offsetInValue = (uint32_t)(fieldOffset - offset);
+ return result;
+ }
+ // The field contains requested value. We want to recursively
+ // traverse the field type to reach a leaf case.
+ auto fieldValue =
+ builder.emitFieldExtract(field->getFieldType(), src, field->getKey());
+ return findLeafValueAtOffset(
+ targetReq,
+ builder,
+ field->getFieldType(),
+ fieldLayout,
+ fieldValue,
+ (uint32_t)(offset - fieldOffset));
+ }
+ }
+ result.leafValue = builder.getIntValue(builder.getUIntType(), 0);
+ result.valueSize = 4;
+ result.offsetInValue = (uint32_t)(offset - layout.size);
+ return result;
+ }
+ break;
+ case kIROp_ArrayType:
+ {
+ auto arrayType = as<IRArrayType>(dataType);
+ auto elementType = arrayType->getElementType();
+ IRSizeAndAlignment elementLayout;
+ CHECK(getNaturalSizeAndAlignment(targetReq, elementType, &elementLayout));
+ if (elementLayout.getStride() == 0)
+ {
+ result.leafValue = builder.getIntValue(builder.getUIntType(), 0);
+ result.valueSize = 4;
+ result.offsetInValue = 0;
+ return result;
+ }
+ uint32_t index = offset / (uint32_t)elementLayout.getStride();
+ auto elementValue = builder.emitElementExtract(
+ elementType, src, builder.getIntValue(builder.getUIntType(), index));
+ return findLeafValueAtOffset(
+ targetReq,
+ builder,
+ elementType,
+ elementLayout,
+ elementValue,
+ (uint32_t)(offset - elementLayout.getStride() * index));
+ }
+ break;
+ case kIROp_VectorType:
+ {
+ auto vectorType = as<IRVectorType>(dataType);
+ auto elementType = vectorType->getElementType();
+ IRSizeAndAlignment elementLayout;
+ CHECK(getNaturalSizeAndAlignment(targetReq, elementType, &elementLayout));
+ uint32_t index =
+ elementLayout.getStride() == 0 ? 0 : (uint32_t)(offset / elementLayout.getStride());
+ auto elementValue = builder.emitElementExtract(
+ elementType, src, builder.getIntValue(builder.getUIntType(), index));
+ return findLeafValueAtOffset(
+ targetReq,
+ builder,
+ elementType,
+ elementLayout,
+ elementValue,
+ (uint32_t)(offset - elementLayout.getStride() * index));
+ }
+ break;
+ case kIROp_MatrixType:
+ {
+ // Note: this code is assuming row major odering.
+ auto matrixType = as<IRMatrixType>(dataType);
+ auto elementType = matrixType->getElementType();
+ SLANG_RELEASE_ASSERT(matrixType->getColumnCount()->op == kIROp_IntLit);
+ auto columnCount = as<IRIntLit>(matrixType->getColumnCount())->value.intVal;
+ auto rowType = builder.getVectorType(elementType, matrixType->getColumnCount());
+ IRSizeAndAlignment rowLayout;
+ CHECK(getNaturalSizeAndAlignment(targetReq, rowType, &rowLayout));
+ uint32_t rowIndex = rowLayout.getStride() == 0
+ ? 0
+ : (uint32_t)(offset / (columnCount * rowLayout.getStride()));
+ auto rowValue = builder.emitElementExtract(
+ rowType, src, builder.getIntValue(builder.getUIntType(), rowIndex));
+ return findLeafValueAtOffset(
+ targetReq,
+ builder,
+ rowType,
+ rowLayout,
+ rowValue,
+ (uint32_t)(offset - rowLayout.getStride() * rowIndex));
+ }
+ break;
+ default:
+ {
+ result.leafValue = src;
+ result.offsetInValue = offset;
+ result.valueSize = (uint32_t)layout.size;
+ return result;
+ }
+ break;
+ }
+}
+
+IRInst* extractByteAtOffset(
+ IRBuilder& builder,
+ TargetRequest* targetReq,
+ IRType* dataType,
+ IRSizeAndAlignment& layout,
+ IRInst* src,
+ uint32_t offset)
+{
+ auto leaf = findLeafValueAtOffset(targetReq, builder, dataType, layout, src, offset);
+ IRType* uintType = nullptr;
+ if (leaf.valueSize <= 4)
+ {
+ uintType = builder.getUIntType();
+ }
+ else
+ {
+ uintType = builder.getUInt64Type();
+ }
+ auto resultValue = builder.emitBitCast(uintType, leaf.leafValue);
+ if (leaf.offsetInValue != 0)
+ {
+ uint32_t shift = leaf.offsetInValue * 8;
+ resultValue = builder.emitShr(uintType, resultValue, builder.getIntValue(uintType, shift));
+
+ resultValue = builder.emitBitAnd(
+ builder.getUIntType(),
+ resultValue, builder.getIntValue(builder.getUIntType(), 0xFF));
+ }
+ return resultValue;
+}
+
+IRInst* extractMultiByteValueAtOffset(
+ IRBuilder& builder,
+ TargetRequest* targetReq,
+ IRType* dataType,
+ IRSizeAndAlignment& layout,
+ IRInst* src,
+ uint32_t size,
+ uint32_t offset)
+{
+ if (size == 1)
+ return extractByteAtOffset(builder, targetReq, dataType, layout, src, offset);
+
+ auto leaf = findLeafValueAtOffset(targetReq, builder, dataType, layout, src, offset);
+ auto resultValue = leaf.leafValue;
+ IRType* uintType = nullptr;
+ if (leaf.valueSize <= 4)
+ {
+ uintType = builder.getUIntType();
+ }
+ else
+ {
+ uintType = builder.getUInt64Type();
+ }
+ if (leaf.valueSize - leaf.offsetInValue >= size)
+ {
+ // The request value is fully contained in the found leaf element.
+ // We can proceed to extract the requested bits from the element.
+ uint32_t shift = leaf.offsetInValue * 8;
+ if (shift > 0)
+ resultValue = builder.emitShr(uintType, resultValue, builder.getIntValue(uintType, shift));
+ uint32_t bitMask = 0;
+ switch (size)
+ {
+ case 1:
+ bitMask = 0xFF;
+ break;
+ case 2:
+ bitMask = 0xFFFFF;
+ break;
+ case 3:
+ bitMask = 0xFFFFFF;
+ break;
+ case 4:
+ bitMask = 0xFFFFFFFF;
+ break;
+ default:
+ break;
+ }
+ if (leaf.valueSize != size)
+ {
+ resultValue =
+ builder.emitBitAnd(uintType, resultValue, builder.getIntValue(uintType, bitMask));
+ }
+ return resultValue;
+ }
+ else
+ {
+ // The requested value crosses the boundaries of different fields.
+ // We need to extract first and second half separately, and combine them together.
+ auto firstHalf = extractMultiByteValueAtOffset(
+ builder, targetReq, dataType, layout, src, size / 2, offset);
+ auto secondHalf = extractMultiByteValueAtOffset(
+ builder, targetReq, dataType, layout, src, size / 2, offset + size / 2);
+ uint32_t shift = (size / 2) * 8;
+ resultValue = builder.emitAdd(
+ builder.getUIntType(),
+ firstHalf,
+ builder.emitShl(
+ builder.getUIntType(),
+ secondHalf,
+ builder.getIntValue(builder.getUIntType(), shift)));
+ return resultValue;
+ }
+}
+
+IRInst* extractValueAtOffset(
+ IRBuilder& builder, TargetRequest* targetReq, IRInst* src, uint32_t offset, uint32_t size)
+{
+ auto dataType = src->getDataType();
+ IRSizeAndAlignment typeLayout;
+ SLANG_RETURN_NULL_ON_FAIL(getNaturalSizeAndAlignment(targetReq, dataType, &typeLayout));
+ if (offset + size > typeLayout.size)
+ {
+ return builder.getIntValue(builder.getIntType(), 0);
+ }
+ return extractMultiByteValueAtOffset(
+ builder, targetReq, dataType, typeLayout, src, size, offset);
+}
+
+} // namespace Slang