// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; namespace ILCompiler { /// <summary> /// Represents an algorithm that computes field layout for intrinsic vector types (Vector64/Vector128/Vector256). /// </summary> public class VectorFieldLayoutAlgorithm : FieldLayoutAlgorithm { private readonly FieldLayoutAlgorithm _fallbackAlgorithm; public VectorFieldLayoutAlgorithm(FieldLayoutAlgorithm fallbackAlgorithm) { _fallbackAlgorithm = fallbackAlgorithm; } public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defType, InstanceLayoutKind layoutKind) { Debug.Assert(IsVectorType(defType)); LayoutInt alignment; if (defType.Name == "Vector64`1"u8) { alignment = new LayoutInt(8); } else if (defType.Name == "Vector128`1"u8) { if (defType.Context.Target.Architecture == TargetArchitecture.ARM) { // The Procedure Call Standard for ARM defaults to 8-byte alignment for __m128 alignment = new LayoutInt(8); } else { alignment = new LayoutInt(16); } } else if (defType.Name == "Vector256`1"u8) { if (defType.Context.Target.Architecture == TargetArchitecture.ARM) { // No such type exists for the Procedure Call Standard for ARM. We will default // to the same alignment as __m128, which is supported by the ABI. alignment = new LayoutInt(8); } else if (defType.Context.Target.Architecture == TargetArchitecture.ARM64) { // The Procedure Call Standard for ARM 64-bit (with SVE support) defaults to // 16-byte alignment for __m256. alignment = new LayoutInt(16); } else if (defType.Context.Target.Architecture == TargetArchitecture.LoongArch64) { // TODO-LoongArch64: Update alignment to proper value when implement LoongArch64 intrinsic. alignment = new LayoutInt(16); } else if (defType.Context.Target.Architecture == TargetArchitecture.RiscV64) { // TODO-RISCV64: Update alignment to proper value when we implement RISC-V intrinsic. // RISC-V Vector Extenstion Intrinsic Document // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/vector_type_infos.adoc alignment = new LayoutInt(16); } else { alignment = new LayoutInt(32); } } else { Debug.Assert(defType.Name == "Vector512`1"u8); if (defType.Context.Target.Architecture == TargetArchitecture.ARM) { // No such type exists for the Procedure Call Standard for ARM. We will default // to the same alignment as __m128, which is supported by the ABI. alignment = new LayoutInt(8); } else if (defType.Context.Target.Architecture == TargetArchitecture.ARM64) { // The Procedure Call Standard for ARM 64-bit (with SVE support) defaults to // 16-byte alignment for __m256. alignment = new LayoutInt(16); } else if (defType.Context.Target.Architecture == TargetArchitecture.LoongArch64) { // TODO-LoongArch64: Update alignment to proper value when implement LoongArch64 intrinsic. alignment = new LayoutInt(16); } else if (defType.Context.Target.Architecture == TargetArchitecture.RiscV64) { // TODO-RISCV64: Update alignment to proper value when we implement RISC-V intrinsic. // RISC-V Vector Extenstion Intrinsic Document // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/vector_type_infos.adoc alignment = new LayoutInt(16); } else { alignment = new LayoutInt(64); } } ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(defType, layoutKind); return new ComputedInstanceFieldLayout { ByteCountUnaligned = layoutFromMetadata.ByteCountUnaligned, ByteCountAlignment = layoutFromMetadata.ByteCountAlignment, FieldAlignment = alignment, FieldSize = layoutFromMetadata.FieldSize, Offsets = layoutFromMetadata.Offsets, LayoutAbiStable = true }; } public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defType, StaticLayoutKind layoutKind) { return _fallbackAlgorithm.ComputeStaticFieldLayout(defType, layoutKind); } public override bool ComputeContainsGCPointers(DefType type) { Debug.Assert(!_fallbackAlgorithm.ComputeContainsGCPointers(type)); return false; } public override bool ComputeContainsByRefs(DefType type) { Debug.Assert(!_fallbackAlgorithm.ComputeContainsByRefs(type)); return false; } public override bool ComputeIsUnsafeValueType(DefType type) { Debug.Assert(!_fallbackAlgorithm.ComputeIsUnsafeValueType(type)); return false; } public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type) { if (type.Context.Target.Architecture == TargetArchitecture.ARM64 && type.Instantiation[0].IsPrimitiveNumeric) { return type.InstanceFieldSize.AsInt switch { 8 => ValueTypeShapeCharacteristics.Vector64Aggregate, 16 => ValueTypeShapeCharacteristics.Vector128Aggregate, 32 => ValueTypeShapeCharacteristics.Vector128Aggregate, 64 => ValueTypeShapeCharacteristics.Vector128Aggregate, _ => ValueTypeShapeCharacteristics.None }; } return ValueTypeShapeCharacteristics.None; } public static bool IsVectorType(DefType type) { return type.IsIntrinsic && type.Namespace == "System.Runtime.Intrinsics"u8 && (type.Name == "Vector64`1"u8 || type.Name == "Vector128`1"u8 || type.Name == "Vector256`1"u8 || type.Name == "Vector512`1"u8); } } } |