diff options
Diffstat (limited to 'source/slang/slang-ir-layout.cpp')
| -rw-r--r-- | source/slang/slang-ir-layout.cpp | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/source/slang/slang-ir-layout.cpp b/source/slang/slang-ir-layout.cpp new file mode 100644 index 000000000..0003d279a --- /dev/null +++ b/source/slang/slang-ir-layout.cpp @@ -0,0 +1,239 @@ +// slang-ir-layout.cpp +#include "slang-ir-layout.h" + +#include "slang-ir-insts.h" + +// This file implements facilities for computing and caching layout +// information on IR types. +// +// Unlike the AST-level layout system, this code currently only +// handles the notion of "natural" layout for IR types, which is +// the layout they use when stored in general-purpose memory +// without additional constraints. +// +// In general, "natural" layout for all targets is assumed to follow +// the same basic rules: +// +// * Scalars are all naturally aligned and have the "obvious" size +// +// * Arrays are laid out by separating elements by their "stride" (size rounded up to alignment) +// +// * Vectors are laid out as arrays of elements +// +// * Matrices are laid out as arrays of rows +// +// * Structures are laid out by packing fields in order, placing each field on the "next" +// suitably aligned offset. The alignment of a structure is the maximum alignment of +// its fields. +// +// Right now this file implements a one-size-fits-all version of natural +// layout that might not be a perfect fit for all targets. In particular +// this code currently assumes: +// +// * The `bool` type is laid out as 4 bytes (equivalent to an `int`) +// +// * The size of a structure or array type is *not* rounded up to a multiple +// of its alignment. This means that fields may be laid out in +// the "tail padding" of previous fields in the same structure. This is +// correct behavior for VK/D3D, but does not match the behavior of typical +// C/C++ compilers. +// +// * All matrices are laid out in row-major order, regardless of any +// settings in user code. +// +// TODO: Addressing the above issues would require extending this file to somehow +// get target-specific layout information as an input. One option would be +// to attach information about "natural" layout on the target to the `IRModuleInst` +// as a decoration, similar to how an LLVM IR module stores a "layout string." + +namespace Slang +{ + +static Result _calcNaturalSizeAndAlignment(IRType* type, IRSizeAndAlignment* outSizeAndAlignment) +{ + switch( type->op ) + { + +#define CASE(TYPE, SIZE, ALIGNMENT) \ + case kIROp_##TYPE##Type: \ + *outSizeAndAlignment = IRSizeAndAlignment(SIZE, ALIGNMENT); \ + return SLANG_OK \ + /* end */ + + // Most base types are "naturally aligned" (meaning alignment and size are the same) +#define BASE(TYPE, SIZE) CASE(TYPE, SIZE, SIZE) + + BASE(Int8, 1); + BASE(UInt8, 1); + + BASE(Int16, 2); + BASE(UInt16, 2); + BASE(Half, 2); + + BASE(Int, 4); + BASE(UInt, 4); + BASE(Float, 4); + + BASE(Int64, 8); + BASE(UInt64, 8); + BASE(Double, 8); + + // We are currently handling `bool` following the HLSL + // precednet of storing it in 4 bytes. + // + // TODO: It would be good to try to make this follow + // per-platform conventions, or at least to be able + // to use a 1-byte encoding where available. + // + BASE(Bool, 4); + + // The Slang `void` type is treated as a zero-byte + // type, so that it does not influence layout at all. + // + CASE(Void, 0, 1); + +#undef CASE + +#undef CASE + + case kIROp_StructType: + { + auto structType = cast<IRStructType>(type); + IRSizeAndAlignment structLayout; + for( auto field : structType->getFields() ) + { + IRSizeAndAlignment fieldTypeLayout; + SLANG_RETURN_ON_FAIL(getNaturalSizeAndAlignment(field->getFieldType(), &fieldTypeLayout)); + + structLayout.size = align(structLayout.size, fieldTypeLayout.alignment); + structLayout.alignment = std::max(structLayout.alignment, fieldTypeLayout.alignment); + + IRIntegerValue fieldOffset = structLayout.size; + if( auto module = type->getModule() ) + { + // If we are in a situation where attaching new + // decorations is possible, then we want to + // cache the field offset on the IR field + // instruction. + // + SharedIRBuilder sharedBuilder; + sharedBuilder.module = module; + sharedBuilder.session = module->getSession(); + + IRBuilder builder; + builder.sharedBuilder = &sharedBuilder; + + auto intType = builder.getIntType(); + builder.addDecoration( + field, + kIROp_NaturalOffsetDecoration, + builder.getIntValue(intType, fieldOffset)); + } + + structLayout.size += fieldTypeLayout.size; + } + *outSizeAndAlignment = structLayout; + return SLANG_OK; + } + break; + + case kIROp_ArrayType: + { + auto arrayType = cast<IRArrayType>(type); + + auto elementCountLit = as<IRIntLit>(arrayType->getElementCount()); + if(!elementCountLit) + return SLANG_FAIL; + auto elementCount = elementCountLit->getValue(); + + if( elementCount == 0 ) + { + *outSizeAndAlignment = IRSizeAndAlignment(0, 1); + return SLANG_OK; + } + + auto elementType = arrayType->getElementType(); + IRSizeAndAlignment elementTypeLayout; + SLANG_RETURN_ON_FAIL(getNaturalSizeAndAlignment(elementType, &elementTypeLayout)); + + auto elementStride = elementTypeLayout.getStride(); + + *outSizeAndAlignment = IRSizeAndAlignment( + elementStride * (elementCount - 1) + elementTypeLayout.size, + elementTypeLayout.alignment); + return SLANG_OK; + } + break; + + default: + return SLANG_FAIL; + } +} + +Result getNaturalSizeAndAlignment(IRType* type, IRSizeAndAlignment* outSizeAndAlignment) +{ + if( auto decor = type->findDecoration<IRNaturalSizeAndAlignmentDecoration>() ) + { + *outSizeAndAlignment = IRSizeAndAlignment(decor->getSize(), (int)decor->getAlignment()); + return SLANG_OK; + } + + IRSizeAndAlignment sizeAndAlignment; + SLANG_RETURN_ON_FAIL(_calcNaturalSizeAndAlignment(type, &sizeAndAlignment)); + + if( auto module = type->getModule() ) + { + SharedIRBuilder sharedBuilder; + sharedBuilder.module = module; + sharedBuilder.session = module->getSession(); + + IRBuilder builder; + builder.sharedBuilder = &sharedBuilder; + + auto intType = builder.getIntType(); + builder.addDecoration( + type, + kIROp_NaturalSizeAndAlignmentDecoration, + builder.getIntValue(intType, sizeAndAlignment.size), + builder.getIntValue(intType, sizeAndAlignment.alignment)); + } + + *outSizeAndAlignment = sizeAndAlignment; + return SLANG_OK; +} + + +Result getNaturalOffset(IRStructField* field, IRIntegerValue* outOffset) +{ + if( auto decor = field->findDecoration<IRNaturalOffsetDecoration>() ) + { + *outOffset = decor->getOffset(); + return SLANG_OK; + } + + // Offsets are computed as part of layout out types, + // so we expect that layout of the "parent" type + // of the field should add an offset to it if + // possible. + + auto structType = as<IRStructType>(field->getParent()); + if(!structType) + return SLANG_FAIL; + + IRSizeAndAlignment structTypeLayout; + SLANG_RETURN_ON_FAIL(getNaturalSizeAndAlignment(structType, &structTypeLayout)); + + if( auto decor = field->findDecoration<IRNaturalOffsetDecoration>() ) + { + *outOffset = decor->getOffset(); + return SLANG_OK; + } + + // If attempting to lay out the parent type didn't + // cause the field to get an offset, then we are + // in an unexpected case with no easy answer. + // + return SLANG_FAIL; +} + +} |
