| # IR Extensions | 
 |  | 
 | The IR can be extended by the various backends as needed. This can be done through | 
 | custom transforms, capabilities, types, intrinsics or through subclasses of other values. | 
 |  | 
 | ## Transforms | 
 | In a few cases we need to make transforms on the IR which are specific to a given backend. | 
 | In these cases the transforms live in the `writer/raise` folder for the backend (e.g. | 
 | `lang/msl/writer/raise/`). These transforms are then added into the `raise.cc` file to run the | 
 | transform when necessary. | 
 |  | 
 | Each transform defines its own configuration if necessary. This is typically done by defining a | 
 | `struct` in the transform `.h` file with a naming matching the transform with a  `Config` suffix. | 
 | (e.g. `ModuleConstant` transform has a `ModuleConstantConfig` structure). When the transform is | 
 | called in `raise` the configuration will be created from the generator configuration. | 
 |  | 
 | Transforms have a set of `Capabilities` they support. Often the list of supported capabilities is | 
 | added to the `.h` file so they can be shared with a fuzzer for the transform. The set of | 
 | capabilities is typically named `k` + transform name + `Capabilities` (e.g. `ModuleConstant` has | 
 | `kModuleConstantCapabilities`). The set of capabilities should be kept as minimal as possible for | 
 | the transform. The capabilities are used to control what extra features the IR validator will allow. | 
 |  | 
 | Transforms all follow a similar pattern of a free function named after the transform and then a | 
 | `State` struct with a `Process` method. The free function will run the validator if necessary and | 
 | then call the `State::Process` method. | 
 |  | 
 | ## Intrinsics | 
 | Each backend has a `.def` file listing the intrinsics for that backend (e.g. `lang/msl/msl.def`). | 
 | These custom intrinsics can be types or instructions. See | 
 | [intrinsic_definition_files.md](intrinsic_defintion_files.md). | 
 |  | 
 | ### Instructions | 
 | The intrinsic instructions typically match the signature of the backend, so may take values which | 
 | don't make sense coming from core IR or WGSL. (For instance, the `GroupNonUniform` SPIR-V | 
 | instructions take a `scope` parameter which doesn't exist, and isn't used, but is there to match the | 
 | API on the SPIR-V side. | 
 |  | 
 | The naming of the intrinsic is set to match the casing of the backend instruction. So, | 
 | `group_non_uniform_s_min` turns into `spirv::BuiltinFn::kGroupNonUniformSMin` which matches to the | 
 | `OpGroupNonUniformSMin` instruction in SPIR-V. | 
 |  | 
 | As instructions are added they need to be added to the `printer.cc` file for the backend in order to | 
 | emit the intrinsic. The instruction also needs to be added to the `builtin_fn.cc.tmpl` file for the | 
 | backend to add to the `GetSideEffects` switch. This lists if there are side effects to the | 
 | instruction which need to be accounted for when doing instruction inlining. | 
 |  | 
 | When these instructions are added to the IR they are done using the `BuiltinCall` class for that | 
 | backend. For example, to call the `kGroupNonUniformBroadcast` intrinsic in the SPIR-V backend we | 
 | call: | 
 |  | 
 | ``` | 
 | b_.Call<spirv::ir::BuiltinCall>(ty.u32(), spirv::BuiltinFn::kGroupNonUniformBroadcast, Vector{id})) | 
 | ``` | 
 |  | 
 | ### Types | 
 | There are a few places to modify to create a new type. First, create the type `.h` and `.cc` files | 
 | in the backend `type/` folder. The type will inherit from `CastableBase<NewType, Type>` where | 
 | `NewType` is the name of the new type. At a minimum the class needs to override: | 
 |  | 
 | ``` | 
 | bool Equals(const UniqueNode& other) const override; | 
 |  | 
 | /// @returns the friendly name for this type | 
 | std::string FriendlyName() const override; | 
 |  | 
 | /// @param ctx the clone context | 
 | /// @returns a clone of this type | 
 | NewType* Clone(core::type::CloneContext& ctx) const override; | 
 | ``` | 
 |  | 
 | The class can also override: | 
 |  | 
 | ``` | 
 | /// @returns the size in bytes of the type. This may include tail padding. | 
 | /// @note opaque types will return a size of 0. | 
 | virtual uint32_t Size() const; | 
 |  | 
 | /// @returns the alignment in bytes of the type. This may include tail | 
 | /// padding. | 
 | /// @note opaque types will return a size of 0. | 
 | virtual uint32_t Align() const; | 
 | ``` | 
 |  | 
 | The `Align` and `Size` overrides are only necessary if the default of size 0 and align 0 do not work | 
 | for the new type. | 
 |  | 
 | The type can then be added to the `.def` file for the backend as `type new_type`. In order to use | 
 | the type in the type matches the `type_matches.h` file for the backend needs to be updated to have a | 
 | `MatchNewType` method and a `BuildNewType` method. The arguments end up matching the values passed | 
 | to the type implicit template parameters. | 
 |  | 
 | The new types can be created in the type manager by calling `ty.Get<spirv::types::NewType>()`, | 
 | providing any needed arguments for the type constructor. | 
 |  | 
 | ### Enums | 
 | When defining custom enums in a given backend an extra attribute is needed to set the correct | 
 | namespace for the enum, otherwise it ends up in the core IR namespace. In the SPIR-V backend we want | 
 | the namespace to be `tint::spirv::type` so we declare enums as: | 
 |  | 
 | ``` | 
 | @ns("spirv::type") enum arrayed { | 
 |   NonArrayed | 
 |   Arrayed | 
 | } | 
 | ``` | 
 |  | 
 | The `@ns("spirv::type")` will put the enum into the correct namespace when emitted. | 
 |  |