All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.scalanative.codegen.MemoryLayout.scala Maven / Gradle / Ivy

There is a newer version: 0.5.5
Show newest version
package scala.scalanative
package codegen

import scala.collection.mutable
import scalanative.util.unsupported
import scalanative.codegen.MemoryLayout.PositionedType
import scala.scalanative.build.Config
import scala.scalanative.linker.ClassRef
import scala.scalanative.linker.Field
import scala.scalanative.nir.Attr.Alignment
import scala.scalanative.build.BuildException
import scala.annotation.tailrec

private[codegen] final case class MemoryLayout(
    size: Long,
    tys: Seq[MemoryLayout.PositionedType]
) {

  /** A list of offsets pointing to inner fields of reference types excluding
   *  object header from the address of memory directly after the object header
   *  end expresed as number of words. Used by the GC for scanning objects.
   *  Terminated with offset=-1
   */
  private[codegen] def referenceFieldsOffsets(implicit
      meta: Metadata
  ): Seq[nir.Val.Int] = {
    import nir.Type._
    val offsets =
      tys.collect {
        // offset in words without object header
        case MemoryLayout.PositionedType(
              _: RefKind | // normal or alligned reference field
              StructValue((_: RefKind) :: ArrayValue(nir.Type.Byte, _) :: Nil),
              offset
            ) =>
          offset.toInt
      }

    (offsets :+ -1).map(nir.Val.Int(_))
  }

  /** A list of offsets pointing to all inner reference types excluding object
   *  header from the start of the object address.
   */
  def fieldOffsets(implicit meta: Metadata): Seq[Long] =
    tys
      .dropWhile(_.ty == meta.layouts.ObjectHeader.layout)
      .map(_.offset)
}

private[scalanative] object MemoryLayout {
  final val BITS_IN_BYTE = 8
  final val BYTES_IN_LONG = 8

  final case class PositionedType(ty: nir.Type, offset: Long)

  def sizeOf(ty: nir.Type)(implicit platform: PlatformInfo): Long =
    ty match {
      case _: nir.Type.RefKind | nir.Type.Nothing | nir.Type.Ptr =>
        platform.sizeOfPtr
      case nir.Type.Size =>
        platform.sizeOfPtr
      case t: nir.Type.PrimitiveKind =>
        math.max(t.width / BITS_IN_BYTE, 1)
      case nir.Type.ArrayValue(ty, n) =>
        sizeOf(ty) * n
      case nir.Type.StructValue(tys) =>
        MemoryLayout(tys).size
      case _ =>
        unsupported(s"sizeof $ty")
    }

  def alignmentOf(ty: nir.Type)(implicit platform: PlatformInfo): Long =
    ty match {
      case nir.Type.Long | nir.Type.Double | nir.Type.Size =>
        platform.sizeOfPtr
      case nir.Type.Nothing | nir.Type.Ptr | _: nir.Type.RefKind =>
        platform.sizeOfPtr
      case t: nir.Type.PrimitiveKind =>
        math.max(t.width / BITS_IN_BYTE, 1)
      case nir.Type.ArrayValue(ty, n) =>
        alignmentOf(ty)
      case nir.Type.StructValue(Seq()) =>
        1
      case nir.Type.StructValue(tys) =>
        tys.map(alignmentOf).max
      case _ =>
        unsupported(s"alignment $ty")
    }

  def align(offset: Long, alignment: Long): Long = {
    val alignmentMask = alignment - 1L
    val padding =
      if ((offset & alignmentMask) == 0L) 0L
      else alignment - (offset & alignmentMask)
    offset + padding
  }

  def apply(
      tys: Seq[nir.Type]
  )(implicit platform: PlatformInfo): MemoryLayout = {
    val pos = mutable.UnrolledBuffer.empty[PositionedType]
    var offset = 0L

    val maxAlign = tys.foldLeft(1L) {
      case (maxAlign, ty) =>
        val align = alignmentOf(ty)

        offset = this.align(offset, align)
        pos += PositionedType(ty, offset)
        offset += sizeOf(ty)

        align.max(maxAlign)
    }

    MemoryLayout(align(offset, maxAlign), pos.toSeq)
  }

  def ofAlignedFields(
      fields: Seq[Field]
  )(implicit platform: PlatformInfo, meta: Metadata): MemoryLayout = {
    import meta.layouts.ObjectHeader

    val pos = mutable.UnrolledBuffer.empty[PositionedType]
    var offset = 0L
    var maxAlign = 1L

    def addPadding(alignment: Int): Unit = {
      val remainingPadding = align(offset, alignment) - offset
      if (remainingPadding > 0) {
        val last = pos.last
        // Update last postion to set correct padding by replaceing the type with struct{ty, array[byte]}
        pos.update(
          pos.indexOf(last),
          last.copy(ty =
            nir.Type.StructValue(
              Seq(
                last.ty,
                nir.Type.ArrayValue(nir.Type.Byte, remainingPadding.toInt)
              )
            )
          )
        )
        offset += remainingPadding
      }
    }

    def addField(ty: nir.Type, fieldAlignment: Option[Int] = None): Unit = {
      val alignment = fieldAlignment.map(_.toLong).getOrElse(alignmentOf(ty))
      maxAlign = maxAlign.max(alignment)

      offset = align(offset, alignment)
      pos += PositionedType(ty, offset)
      offset += sizeOf(ty)
    }

    lazy val dynamicAlignmentWidth = {
      val propName =
        "scala.scalanative.meta.linktimeinfo.contendedPaddingWidth"
      meta.analysis.resolvedVals
        .get(propName)
        .collectFirst { case nir.Val.Int(value) => value }
        .getOrElse(
          throw new BuildException(
            s"Unable to resolve size of dynamic field alignment, linktime property not found: $propName"
          )
        )
    }
    def resolveAlignWidth(align: Alignment): Int = align.size match {
      case nir.Attr.Alignment.linktimeResolved => dynamicAlignmentWidth
      case fixedWidth                          => fixedWidth
    }

    def isGroupAligned(field: Field) =
      field.attrs.align.flatMap(_.group).isDefined

    // fields should be already ordered by group names
    @tailrec def loop(fields: List[Field]): Unit = fields match {
      case Nil => ()
      case field :: tail =>
        val alignInfo = field.attrs.align
        val groupName = alignInfo.flatMap(_.group)
        val groupTail =
          if (isGroupAligned(field))
            tail.takeWhile(_.attrs.align.flatMap(_.group) == groupName)
          else Nil
        val headAlignSize = alignInfo.map(resolveAlignWidth)
        // Align size is equal to maximal alignment of all fields in the group
        val alignSize = headAlignSize.map {
          groupTail.foldLeft(_) {
            case (maxSize, field) =>
              field.attrs.align
                .map(resolveAlignWidth)
                .map(_.max(maxSize))
                .getOrElse(maxSize)
          }
        }

        alignSize.foreach(addPadding)
        addField(field.ty, alignSize)
        groupTail.foreach(field => addField(field.ty))
        alignSize.foreach(addPadding)

        loop(tail.drop(groupTail.size))
    }

    addField(ObjectHeader.layout)
    loop(fields.toList)
    MemoryLayout(align(offset, maxAlign), pos.toSeq)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy