diff --git a/hail/src/main/scala/is/hail/expr/types/physical/ComplexPType.scala b/hail/src/main/scala/is/hail/expr/types/physical/ComplexPType.scala index 1d229cfeba6..3bb74ee4f7b 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/ComplexPType.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/ComplexPType.scala @@ -1,6 +1,7 @@ package is.hail.expr.types.physical -import is.hail.annotations.UnsafeOrdering +import is.hail.annotations.{Region, UnsafeOrdering} +import is.hail.asm4s.{Code, MethodBuilder} abstract class ComplexPType extends PType { val representation: PType @@ -14,4 +15,21 @@ abstract class ComplexPType extends PType { override def fundamentalType: PType = representation.fundamentalType override def containsPointers: Boolean = representation.containsPointers + + override def storeShallowAtOffset(dstAddress: Code[Long], valueAddress: Code[Long]): Code[Unit] = + this.representation.storeShallowAtOffset(dstAddress, valueAddress) + + override def storeShallowAtOffset(dstAddress: Long, valueAddress: Long) { + this.representation.storeShallowAtOffset(dstAddress, valueAddress) + } + + override def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = { + assert(this isOfType srcPType) + + val srcRepPType = srcPType.asInstanceOf[ComplexPType].representation + + this.representation.copyFromType(mb, region, srcRepPType, srcAddress, forceDeep) + } + + override def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = ??? } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PArrayBackedContainer.scala b/hail/src/main/scala/is/hail/expr/types/physical/PArrayBackedContainer.scala index 2600dddba12..65867673b4b 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PArrayBackedContainer.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PArrayBackedContainer.scala @@ -1,7 +1,7 @@ package is.hail.expr.types.physical import is.hail.annotations.{Region, UnsafeOrdering} -import is.hail.asm4s.{Code, MethodBuilder} +import is.hail.asm4s.{???, Code, MethodBuilder} import is.hail.expr.ir.EmitMethodBuilder trait PArrayBackedContainer extends PContainer { @@ -110,6 +110,9 @@ trait PArrayBackedContainer extends PContainer { def loadElement(aoff: Long, length: Int, i: Int): Long = arrayRep.loadElement(aoff, length, i) + def loadElementAddress(aoff: Code[Long], length: Code[Int], i: Code[Int]): Code[Long] = + arrayRep.loadElementAddress(aoff, length, i) + def loadElement(region: Region, aoff: Long, length: Int, i: Int): Long = arrayRep.loadElement(region, aoff, length, i) @@ -164,7 +167,29 @@ trait PArrayBackedContainer extends PContainer { def copyFrom(mb: MethodBuilder, region: Code[Region], srcOff: Code[Long]): Code[Long] = arrayRep.copyFrom(mb, region, srcOff) - override def unsafeOrdering: UnsafeOrdering = unsafeOrdering(this) + override def unsafeOrdering: UnsafeOrdering = + unsafeOrdering(this) + + override def unsafeOrdering(rightType: PType): UnsafeOrdering = + arrayRep.unsafeOrdering(rightType) + + override def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = { + assert(srcPType.isInstanceOf[PArrayBackedContainer]) + this.arrayRep.copyFromType(mb, region, srcPType.asInstanceOf[PArrayBackedContainer].arrayRep, srcAddress, forceDeep) + } + + override def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = ??? + + override def storeShallowAtOffset(dstAddress: Code[Long], valueAddress: Code[Long]): Code[Unit] = + this.arrayRep.storeShallowAtOffset(dstAddress, valueAddress) + + override def storeShallowAtOffset(dstAddress: Long, valueAddress: Long) { + this.arrayRep.storeShallowAtOffset(dstAddress, valueAddress) + } + + def nextElementAddress(currentOffset: Long) = + arrayRep.nextElementAddress(currentOffset) - override def unsafeOrdering(rightType: PType): UnsafeOrdering = arrayRep.unsafeOrdering(rightType) + def nextElementAddress(currentOffset: Code[Long]) = + arrayRep.nextElementAddress(currentOffset) } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PBaseStruct.scala b/hail/src/main/scala/is/hail/expr/types/physical/PBaseStruct.scala index cdbd31b17ac..9e012f7177e 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PBaseStruct.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PBaseStruct.scala @@ -74,6 +74,7 @@ abstract class PBaseStruct extends PType { } def identBase: String + def _asIdent: String = { val sb = new StringBuilder sb.append(identBase) @@ -147,7 +148,30 @@ abstract class PBaseStruct extends PType { region.allocate(alignment, byteSize) } - def allocate(region: Code[Region]): Code[Long] = region.allocate(alignment, byteSize) + def allocate(region: Code[Region]): Code[Long] = + region.allocate(alignment, byteSize) + + def copyFrom(region: Region, srcAddress: Long): Long = { + val destAddress = this.allocate(region) + this.storeShallowAtOffset(destAddress, srcAddress) + destAddress + } + + def copyFrom(mb: MethodBuilder, region: Code[Region], srcAddress: Code[Long]): Code[Long] = { + val destAddress = mb.newField[Long] + Code( + destAddress := this.allocate(region), + this.storeShallowAtOffset(destAddress, srcAddress), + destAddress + ) + } + + override def storeShallowAtOffset(destAddress: Code[Long], srcAddress: Code[Long]): Code[Unit] = + Region.copyFrom(srcAddress, destAddress, this.byteSize) + + override def storeShallowAtOffset(destAddress: Long, srcAddress: Long) { + Region.copyFrom(srcAddress, destAddress, this.byteSize) + } def initialize(structAddress: Long, setMissing: Boolean = false): Unit = { if (allFieldsRequired) { @@ -217,11 +241,11 @@ abstract class PBaseStruct extends PType { def setFieldPresent(region: Code[Region], offset: Code[Long], fieldIdx: Int): Code[Unit] = setFieldPresent(offset, fieldIdx) - def fieldOffset(offset: Long, fieldIdx: Int): Long = - offset + byteOffsets(fieldIdx) + def fieldOffset(structAddress: Long, fieldIdx: Int): Long = + structAddress + byteOffsets(fieldIdx) - def fieldOffset(offset: Code[Long], fieldIdx: Int): Code[Long] = - offset + byteOffsets(fieldIdx) + def fieldOffset(structAddress: Code[Long], fieldIdx: Int): Code[Long] = + structAddress + byteOffsets(fieldIdx) def loadField(rv: RegionValue, fieldIdx: Int): Long = loadField(rv.region, rv.offset, fieldIdx) @@ -248,5 +272,107 @@ abstract class PBaseStruct extends PType { } } + def deepCopyFromAddress(mb: MethodBuilder, region: Code[Region], srcStructAddress: Code[Long]): Code[Long] = { + val dstAddress = mb.newField[Long] + Code( + dstAddress := this.copyFrom(mb, region, srcStructAddress), + this.deepPointerCopy(mb, region, dstAddress), + dstAddress + ) + } + + def deepPointerCopy(mb: MethodBuilder, region: Code[Region], dstStructAddress: Code[Long]): Code[Unit] = { + var c: Code[Unit] = Code._empty + + var i = 0 + while(i < this.size) { + val dstFieldType = this.fields(i).typ.fundamentalType + if(dstFieldType.containsPointers) { + val dstFieldAddress = mb.newField[Long] + c = Code( + c, + this.isFieldDefined(dstStructAddress, i).orEmpty( + Code( + dstFieldAddress := this.fieldOffset(dstStructAddress, i), + dstFieldType match { + case t@(_: PBinary | _: PArray) => + t.storeShallowAtOffset(dstFieldAddress, t.copyFromType(mb, region, dstFieldType, Region.loadAddress(dstFieldAddress))) + case t: PBaseStruct => + t.deepPointerCopy(mb, region, dstFieldAddress) + case t: PType => + fatal(s"Field type isn't supported ${t}") + } + ) + ) + ) + } + i += 1 + } + + c + } + + override def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcStructAddress: Code[Long], + forceDeep: Boolean): Code[Long] = { + assert(srcPType.isInstanceOf[PBaseStruct]) + + val sourceType = srcPType.asInstanceOf[PBaseStruct] + + assert(sourceType.size == this.size) + + val sourceFundamentalTypes = sourceType.fields.map(_.typ.fundamentalType) + if(this.fields.map(_.typ.fundamentalType) == sourceFundamentalTypes) { + if(!forceDeep) { + return srcStructAddress + } + + return this.deepCopyFromAddress(mb, region, srcStructAddress) + } + + val dstStructAddress = mb.newField[Long] + var loop: Code[_] = Code() + var i = 0 + while(i < this.size) { + val dstField = this.fields(i) + val srcField = sourceType.fields(i) + + assert((dstField.typ.required <= srcField.typ.required) && (dstField.typ isOfType srcField.typ) && (dstField.name == srcField.name) && (dstField.index == srcField.index)) + + val srcFieldType = srcField.typ.fundamentalType + val dstFieldType = dstField.typ.fundamentalType + + val body = srcFieldType.storeShallowAtOffset( + this.fieldOffset(dstStructAddress, dstField.index), + dstFieldType.copyFromType( + mb, + region, + srcFieldType, + sourceType.loadField(srcStructAddress, srcField.index), + forceDeep + ) + ) + + if(!srcFieldType.required) { + loop = Code(loop, sourceType.isFieldMissing(srcStructAddress, srcField.index).mux( + this.setFieldMissing(dstStructAddress, dstField.index), + body + )) + } else { + loop = Code(loop, body) + } + + i+=1 + } + + Code( + dstStructAddress := this.allocate(region), + this.stagedInitialize(dstStructAddress), + loop, + dstStructAddress + ) + } + + override def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = ??? + override def containsPointers: Boolean = types.exists(_.containsPointers) } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PBinary.scala b/hail/src/main/scala/is/hail/expr/types/physical/PBinary.scala index b8fd772d80f..cc7ccf62cd0 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PBinary.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PBinary.scala @@ -79,13 +79,19 @@ object PBinary { def contentByteSize(length: Code[Int]): Code[Long] = PCanonicalBinary.contentByteSize(length) - def loadLength(boff: Long): Int = PCanonicalBinary.loadLength(boff) + def loadLength(bAddress: Long): Int = PCanonicalBinary.loadLength(bAddress) - def loadLength(region: Region, boff: Long): Int = PCanonicalBinary.loadLength(boff) + def loadLength(region: Region, bAddress: Long): Int = PCanonicalBinary.loadLength(bAddress) - def loadLength(boff: Code[Long]): Code[Int] = PCanonicalBinary.loadLength(boff) + def loadLength(bAddress: Code[Long]): Code[Int] = PCanonicalBinary.loadLength(bAddress) - def loadLength(region: Code[Region], boff: Code[Long]): Code[Int] = PCanonicalBinary.loadLength(boff) + def loadLength(region: Code[Region], bAddress: Code[Long]): Code[Int] = PCanonicalBinary.loadLength(bAddress) + + def loadBytes(bAddress: Code[Long]): Code[Array[Byte]] = + Region.loadBytes(PBinary.bytesOffset(bAddress), PBinary.loadLength(bAddress)) + + def loadBytes(bAddress: Long): Array[Byte] = + Region.loadBytes(PBinary.bytesOffset(bAddress), PBinary.loadLength(bAddress)) def storeLength(boff: Long, len: Int): Unit = PCanonicalBinary.storeLength(boff, len) diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PBoolean.scala b/hail/src/main/scala/is/hail/expr/types/physical/PBoolean.scala index 9bb9b72f831..9607f0ad664 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PBoolean.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PBoolean.scala @@ -1,7 +1,7 @@ package is.hail.expr.types.physical import is.hail.annotations.{Region, UnsafeOrdering, _} -import is.hail.asm4s.Code +import is.hail.asm4s.{Code, MethodBuilder} import is.hail.expr.ir.EmitMethodBuilder import is.hail.expr.types.virtual.TBoolean @@ -34,6 +34,18 @@ class PBoolean(override val required: Boolean) extends PType { } override def byteSize: Long = 1 + + def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]): Code[Unit] = + Region.storeBoolean(dstAddress, Region.loadBoolean(srcAddress)) + + def storeShallowAtOffset(dstAddress: Long, srcAddress: Long) = + Region.storeBoolean(dstAddress, Region.loadBoolean(srcAddress)) + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = + srcAddress + + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = + srcAddress } object PBoolean { diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalArray.scala b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalArray.scala index 0fac244c6dc..0be0ccced54 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalArray.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalArray.scala @@ -8,6 +8,7 @@ import is.hail.asm4s.joinpoint._ import is.hail.expr.ir.EmitMethodBuilder import is.hail.utils._ +// This is a pointer array, whose byteSize is the size of its pointer final case class PCanonicalArray(elementType: PType, required: Boolean = false) extends PArray { def _asIdent = s"array_of_${elementType.asIdent}" @@ -162,6 +163,12 @@ final case class PCanonicalArray(elementType: PType, required: Boolean = false) def elementOffsetInRegion(region: Code[Region], aoff: Code[Long], i: Code[Int]): Code[Long] = elementOffset(aoff, loadLength(region, aoff), i) + def nextElementAddress(currentOffset: Long) = + currentOffset + elementByteSize + + def nextElementAddress(currentOffset: Code[Long]) = + currentOffset + elementByteSize + def loadElement(aoff: Long, length: Int, i: Int): Long = { val off = elementOffset(aoff, length, i) elementType.fundamentalType match { @@ -174,7 +181,10 @@ final case class PCanonicalArray(elementType: PType, required: Boolean = false) def loadElement(region: Region, aoff: Long, i: Int): Long = loadElement(aoff, loadLength(aoff), i) - def loadElement(region: Code[Region], aoff: Code[Long], length: Code[Int], i: Code[Int]): Code[Long] = { + def loadElement(region: Code[Region], aoff: Code[Long], length: Code[Int], i: Code[Int]): Code[Long] = + loadElementAddress(aoff, length, i) + + def loadElementAddress(aoff: Code[Long], length: Code[Int], i: Code[Int]): Code[Long] = { val off = elementOffset(aoff, length, i) elementType.fundamentalType match { case _: PArray | _: PBinary => Region.loadAddress(off) @@ -182,10 +192,8 @@ final case class PCanonicalArray(elementType: PType, required: Boolean = false) } } - def loadElement(region: Code[Region], aoff: Code[Long], i: Code[Int]): Code[Long] = { - val length = loadLength(region, aoff) - loadElement(region, aoff, length, i) - } + def loadElement(region: Code[Region], aoff: Code[Long], i: Code[Int]): Code[Long] = + loadElement(region, aoff, loadLength(aoff), i) def allocate(region: Region, length: Int): Long = { region.allocate(contentsAlignment, contentsByteSize(length)) @@ -316,30 +324,30 @@ final case class PCanonicalArray(elementType: PType, required: Boolean = false) } } - def hasMissingValues(sourceOffset: Code[Long]): Code[Boolean] = { + def hasMissingValues(srcAddress: Code[Long]): Code[Boolean] = { if(elementType.required) { return const(false) } - Region.containsNonZeroBits(sourceOffset + lengthHeaderBytes, loadLength(sourceOffset).toL) + Region.containsNonZeroBits(srcAddress + lengthHeaderBytes, loadLength(srcAddress).toL) } - def checkedConvertFrom(mb: EmitMethodBuilder, r: Code[Region], sourceOffset: Code[Long], sourceType: PContainer, msg: String): Code[Long] = { + def checkedConvertFrom(mb: EmitMethodBuilder, r: Code[Region], srcAddress: Code[Long], sourceType: PContainer, msg: String): Code[Long] = { assert(sourceType.elementType.isPrimitive && this.isOfType(sourceType)) if (sourceType.elementType.required == this.elementType.required) { - return sourceOffset + return srcAddress } Code( - sourceType.hasMissingValues(sourceOffset).orEmpty(Code._fatal(msg)), { + sourceType.hasMissingValues(srcAddress).orEmpty(Code._fatal(msg)), { val newOffset = mb.newField[Long] - val len = sourceType.loadLength(sourceOffset) + val len = sourceType.loadLength(srcAddress) Code( newOffset := allocate(r, len), stagedInitialize(newOffset, len), - Region.copyFrom(sourceType.firstElementOffset(sourceOffset, len), firstElementOffset(newOffset, len), len.toL * elementByteSize), + Region.copyFrom(sourceType.firstElementOffset(srcAddress, len), firstElementOffset(newOffset, len), len.toL * elementByteSize), newOffset ) } @@ -360,4 +368,111 @@ final case class PCanonicalArray(elementType: PType, required: Boolean = false) destOff ) } + + override def storeShallowAtOffset(dstAddress: Code[Long], valueAddress: Code[Long]): Code[Unit] = + Region.storeAddress(dstAddress, valueAddress) + + override def storeShallowAtOffset(dstAddress: Long, valueAddress: Long) { + Region.storeAddress(dstAddress, valueAddress) + } + + def deepCopyFromAddress(mb: MethodBuilder, region: Code[Region], srcArrayAddress: Code[Long]): Code[Long] = { + val dstAddress = mb.newField[Long] + Code( + dstAddress := this.copyFrom(mb, region, srcArrayAddress), + this.deepPointerCopy(mb, region, dstAddress), + dstAddress + ) + } + + def deepPointerCopy(mb: MethodBuilder, region: Code[Region], dstAddress: Code[Long]): Code[Unit] = { + if(!this.elementType.fundamentalType.containsPointers) { + return Code._empty + } + + val numberOfElements = mb.newLocal[Int] + val currentIdx = mb.newLocal[Int] + val currentElementAddress = mb.newField[Long] + Code( + currentIdx := const(0), + numberOfElements := this.loadLength(dstAddress), + Code.whileLoop(currentIdx < numberOfElements, + this.isElementDefined(dstAddress, currentIdx).orEmpty( + Code( + currentElementAddress := this.elementOffset(dstAddress, numberOfElements, currentIdx), + this.elementType.fundamentalType match { + case t@(_: PBinary | _: PArray) => + t.storeShallowAtOffset(currentElementAddress, t.copyFromType(mb, region, t, Region.loadAddress(currentElementAddress))) + case t: PBaseStruct => + t.deepPointerCopy(mb, region, currentElementAddress) + case t: PType => fatal(s"Type isn't supported ${t}") + } + ) + ), + currentIdx := currentIdx + const(1) + ) + ) + } + + override def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean = false): Code[Long] = { + assert(srcPType.isInstanceOf[PArray]) + + val sourceType = srcPType.asInstanceOf[PArray] + val sourceElementType = sourceType.elementType.fundamentalType + val destElementType = this.elementType.fundamentalType + + if (sourceElementType != destElementType) { + assert(destElementType.required <= sourceElementType.required && sourceElementType.isOfType(destElementType)) + } else { + if(!forceDeep) { + return srcAddress + } + + return this.deepCopyFromAddress(mb, region, srcAddress) + } + + val dstAddress = mb.newField[Long] + val numberOfElements = mb.newLocal[Int] + val currentElementAddress = mb.newLocal[Long] + val currentIdx = mb.newLocal[Int] + + val init = Code( + numberOfElements := sourceType.loadLength(srcAddress), + dstAddress := this.allocate(region, numberOfElements), + this.stagedInitialize(dstAddress, numberOfElements), + currentElementAddress := this.firstElementOffset(dstAddress, numberOfElements), + currentIdx := const(0) + ) + + var loop: Code[Unit] = + destElementType.storeShallowAtOffset( + currentElementAddress, + destElementType.copyFromType( + mb, + region, + sourceElementType, + sourceType.loadElementAddress(srcAddress, numberOfElements, currentIdx), + forceDeep + ) + ) + + if(!sourceElementType.required) { + loop = sourceType.isElementMissing(srcAddress, currentIdx).mux( + this.setElementMissing(dstAddress, currentIdx), + loop + ) + } + + Code( + init, + Code.whileLoop(currentIdx < numberOfElements, + loop, + currentElementAddress := this.nextElementAddress(currentElementAddress), + currentIdx := currentIdx + const(1) + ), + dstAddress + ) + } + + override def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = ??? } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalBinary.scala b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalBinary.scala index f53c5235642..7c3fd5c379e 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalBinary.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalBinary.scala @@ -12,8 +12,36 @@ class PCanonicalBinary(val required: Boolean) extends PBinary { override def byteSize: Long = 8 + override def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = { + if(this == srcPType && !forceDeep) { + return srcAddress + } + + assert(this isOfType srcPType) + + val dstAddress = mb.newField[Long] + val length = mb.newLocal[Int] + + Code( + length := PCanonicalBinary.loadLength(region, srcAddress), + dstAddress := PCanonicalBinary.allocate(region, length), + Region.copyFrom(srcAddress, dstAddress, PCanonicalBinary.contentByteSize(length)), + dstAddress + ) + } + + override def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = ??? + override def containsPointers: Boolean = true + override def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]): Code[Unit] = { + Region.storeAddress(dstAddress, srcAddress) + } + + override def storeShallowAtOffset(dstAddress: Long, srcAddress: Long){ + Region.storeAddress(dstAddress, srcAddress) + } + override def _pretty(sb: StringBuilder, indent: Int, compact: Boolean): Unit = sb.append("PCBinary") } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalCall.scala b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalCall.scala index 700c2186465..9bcf7bfbdd2 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalCall.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalCall.scala @@ -1,10 +1,11 @@ package is.hail.expr.types.physical +import is.hail.annotations.Region import is.hail.asm4s._ import is.hail.expr.ir.EmitFunctionBuilder import is.hail.variant.Genotype -final case class PCanonicalCall(required: Boolean) extends PCall { +final case class PCanonicalCall(required: Boolean = false) extends PCall { def _asIdent = "call" override def _pretty(sb: StringBuilder, indent: Int, compact: Boolean): Unit = sb.append("PCCall") @@ -52,5 +53,4 @@ final case class PCanonicalCall(required: Boolean) extends PCall { ) ) } - } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalInterval.scala b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalInterval.scala index ae0b2aad746..ec4cc727f17 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalInterval.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalInterval.scala @@ -1,13 +1,7 @@ package is.hail.expr.types.physical -import is.hail.annotations.{CodeOrdering, _} -import is.hail.asm4s.Code -import is.hail.check.Gen -import is.hail.expr.ir.EmitMethodBuilder -import is.hail.expr.types.virtual.TInterval -import is.hail.utils._ - -import scala.reflect.{ClassTag, classTag} +import is.hail.annotations._ +import is.hail.asm4s.{Code, MethodBuilder} final case class PCanonicalInterval(pointType: PType, override val required: Boolean = false) extends PInterval { def _asIdent = s"interval_of_${pointType.asIdent}" diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalLocus.scala b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalLocus.scala index 5e26bfeef47..08dd7c04849 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalLocus.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalLocus.scala @@ -1,11 +1,9 @@ + package is.hail.expr.types.physical import is.hail.variant.ReferenceGenome - import is.hail.annotations._ -import is.hail.asm4s.{Code, coerce} -import is.hail.backend.BroadcastValue +import is.hail.asm4s.{Code, MethodBuilder, coerce} import is.hail.expr.ir.EmitMethodBuilder -import is.hail.expr.types.virtual.TLocus import is.hail.utils._ import is.hail.variant._ @@ -27,23 +25,23 @@ object PCanonicalLocus { } final case class PCanonicalLocus(rgBc: BroadcastRG, required: Boolean = false) extends PLocus { - def rg: ReferenceGenome = rgBc.value + def rg: ReferenceGenome = rgBc.value - def _asIdent = "locus" + def _asIdent = "locus" - override def _pretty(sb: StringBuilder, indent: Call, compact: Boolean): Unit = sb.append(s"PCLocus($rg)") + override def _pretty(sb: StringBuilder, indent: Call, compact: Boolean): Unit = sb.append(s"PCLocus($rg)") - def copy(required: Boolean = this.required) = PCanonicalLocus(this.rgBc, required) + def copy(required: Boolean = this.required) = PCanonicalLocus(this.rgBc, required) - val representation: PStruct = PCanonicalLocus.representation(required) + val representation: PStruct = PCanonicalLocus.representation(required) - def contig(region: Code[Region], off: Code[Long]): Code[Long] = representation.loadField(region, off, 0) + def contig(region: Code[Region], off: Code[Long]): Code[Long] = representation.loadField(region, off, 0) - lazy val contigType: PString = representation.field("contig").typ.asInstanceOf[PString] + lazy val contigType: PString = representation.field("contig").typ.asInstanceOf[PString] - def position(region: Code[Region], off: Code[Long]): Code[Int] = Region.loadInt(representation.loadField(region, off, 1)) + def position(region: Code[Region], off: Code[Long]): Code[Int] = Region.loadInt(representation.loadField(region, off, 1)) - lazy val positionType: PInt32 = representation.field("position").typ.asInstanceOf[PInt32] + lazy val positionType: PInt32 = representation.field("position").typ.asInstanceOf[PInt32] // FIXME: Remove when representation of contig/position is a naturally-ordered Long override def unsafeOrdering(): UnsafeOrdering = { diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalNDArray.scala b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalNDArray.scala index 2e5cd9250e2..8226ae1d068 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalNDArray.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalNDArray.scala @@ -206,4 +206,22 @@ final case class PCanonicalNDArray(elementType: PType, nDims: Int, required: Boo srvb.end() )) } + + override def storeShallowAtOffset(dstAddress: Code[Long], valueAddress: Code[Long]): Code[Unit] = + this.representation.storeShallowAtOffset(dstAddress, valueAddress) + + override def storeShallowAtOffset(dstAddress: Long, valueAddress: Long) { + this.representation.storeShallowAtOffset(dstAddress, valueAddress) + } + + override def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = { + assert(srcPType.isInstanceOf[PNDArray]) + val sourceNDPType = srcPType.asInstanceOf[PNDArray] + + assert(this.elementType == sourceNDPType.elementType && this.nDims == sourceNDPType.nDims) + + this.representation.copyFromType(mb, region, sourceNDPType.representation, srcAddress, forceDeep) + } + + override def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = ??? } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalString.scala b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalString.scala index 45cc5d1af3e..ce809015345 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalString.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalString.scala @@ -1,7 +1,7 @@ package is.hail.expr.types.physical import is.hail.annotations.Region -import is.hail.asm4s.Code +import is.hail.asm4s.{???, Code, MethodBuilder} case object PCanonicalStringOptional extends PCanonicalString(false) case object PCanonicalStringRequired extends PCanonicalString(true) @@ -15,7 +15,23 @@ abstract class PCanonicalString(val required: Boolean) extends PString { lazy val binaryFundamentalType: PBinary = PBinary(required) + override def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcOffset: Code[Long], forceDeep: Boolean): Code[Long] = { + assert(srcPType isOfType this) + this.fundamentalType.copyFromType( + mb, region, srcPType.asInstanceOf[PString].fundamentalType, srcOffset, forceDeep + ) + } + + override def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = ??? + override def containsPointers: Boolean = true + + override def storeShallowAtOffset(dstAddress: Code[Long], valueAddress: Code[Long]): Code[Unit] = + this.fundamentalType.storeShallowAtOffset(dstAddress, valueAddress) + + override def storeShallowAtOffset(dstAddress: Long, valueAddress: Long) { + this.fundamentalType.storeShallowAtOffset(dstAddress, valueAddress) + } } object PCanonicalString { @@ -23,26 +39,21 @@ object PCanonicalString { def unapply(t: PString): Option[Boolean] = Option(t.required) - def loadString(boff: Long): String = { - val length = PBinary.loadLength(boff) - new String(Region.loadBytes(PBinary.bytesOffset(boff), length)) - } + def loadString(bAddress: Long): String = + new String(PBinary.loadBytes(bAddress)) def loadString(region: Region, boff: Long): String = loadString(boff) - def loadString(boff: Code[Long]): Code[String] = { - val length = PBinary.loadLength(boff) - Code.newInstance[String, Array[Byte]]( - Region.loadBytes(PBinary.bytesOffset(boff), length)) - } + def loadString(bAddress: Code[Long]): Code[String] = + Code.newInstance[String, Array[Byte]](PBinary.loadBytes(bAddress)) - def loadString(region: Code[Region], boff: Code[Long]): Code[String] = - loadString(boff) + def loadString(region: Code[Region], bAddress: Code[Long]): Code[String] = + loadString(bAddress) - def loadLength(region: Region, boff: Long): Int = - PBinary.loadLength(region, boff) + def loadLength(region: Region, bAddress: Long): Int = + PBinary.loadLength(region, bAddress) - def loadLength(region: Code[Region], boff: Code[Long]): Code[Int] = - PBinary.loadLength(region, boff) + def loadLength(region: Code[Region], bAddress: Code[Long]): Code[Int] = + PBinary.loadLength(region, bAddress) } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalStruct.scala b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalStruct.scala index 8ce596b7d17..bc605c66ab5 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalStruct.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PCanonicalStruct.scala @@ -3,9 +3,10 @@ package is.hail.expr.types.physical import is.hail.annotations._ import is.hail.asm4s.Code import is.hail.expr.types.BaseStruct -import is.hail.expr.types.virtual.{Field, TStruct, Type} +import is.hail.expr.types.virtual.Type import is.hail.utils._ import org.apache.spark.sql.Row + import collection.JavaConverters._ object PCanonicalStruct { diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PContainer.scala b/hail/src/main/scala/is/hail/expr/types/physical/PContainer.scala index 7b8d04cc9fc..95af5c78db5 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PContainer.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PContainer.scala @@ -83,6 +83,8 @@ abstract class PContainer extends PIterable { def loadElement(region: Region, aoff: Long, i: Int): Long + def loadElementAddress(aoff: Code[Long], length: Code[Int], i: Code[Int]): Code[Long] + def loadElement(region: Code[Region], aoff: Code[Long], length: Code[Int], i: Code[Int]): Code[Long] def loadElement(region: Code[Region], aoff: Code[Long], i: Code[Int]): Code[Long] @@ -110,4 +112,8 @@ abstract class PContainer extends PIterable { def hasMissingValues(sourceOffset: Code[Long]): Code[Boolean] def checkedConvertFrom(mb: EmitMethodBuilder, r: Code[Region], sourceOffset: Code[Long], sourceType: PContainer, msg: String): Code[Long] + + def nextElementAddress(currentOffset: Long): Long + + def nextElementAddress(currentOffset: Code[Long]): Code[Long] } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PFloat32.scala b/hail/src/main/scala/is/hail/expr/types/physical/PFloat32.scala index a06f857320c..c4757d23e7d 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PFloat32.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PFloat32.scala @@ -59,6 +59,18 @@ class PFloat32(override val required: Boolean) extends PNumeric { override def multiply(a: Code[_], b: Code[_]): Code[PFloat32] = { coerce[PFloat32](coerce[Float](a) * coerce[Float](b)) } + + def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]): Code[Unit] = + Region.storeFloat(dstAddress, Region.loadFloat(srcAddress)) + + def storeShallowAtOffset(dstAddress: Long, srcAddress: Long) = + Region.storeFloat(dstAddress, Region.loadFloat(srcAddress)) + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = + srcAddress + + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = + srcAddress } object PFloat32 { diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PFloat64.scala b/hail/src/main/scala/is/hail/expr/types/physical/PFloat64.scala index 7dc34a9da4a..d613d9d846b 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PFloat64.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PFloat64.scala @@ -59,6 +59,18 @@ class PFloat64(override val required: Boolean) extends PNumeric { override def multiply(a: Code[_], b: Code[_]): Code[PFloat64] = { coerce[PFloat64](coerce[Double](a) * coerce[Double](b)) } + + def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]) = + Region.storeDouble(dstAddress, Region.loadDouble(srcAddress)) + + def storeShallowAtOffset(dstAddress: Long, srcAddress: Long) = + Region.storeDouble(dstAddress, Region.loadDouble(srcAddress)) + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = + srcAddress + + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = + srcAddress } object PFloat64 { diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PInt32.scala b/hail/src/main/scala/is/hail/expr/types/physical/PInt32.scala index e5fa892ba58..17258957ca3 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PInt32.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PInt32.scala @@ -56,6 +56,18 @@ class PInt32(override val required: Boolean) extends PIntegral { override def multiply(a: Code[_], b: Code[_]): Code[PInt32] = { coerce[PInt32](coerce[Int](a) * coerce[Int](b)) } + + def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]) = + Region.storeInt(dstAddress, Region.loadInt(srcAddress)) + + def storeShallowAtOffset(dstAddress: Long, srcAddress: Long) = + Region.storeInt(dstAddress, Region.loadInt(srcAddress)) + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = + srcAddress + + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = + srcAddress } object PInt32 { diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PInt64.scala b/hail/src/main/scala/is/hail/expr/types/physical/PInt64.scala index 5cb00e51aa5..b898a127fcc 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PInt64.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PInt64.scala @@ -57,6 +57,18 @@ class PInt64(override val required: Boolean) extends PIntegral { override def multiply(a: Code[_], b: Code[_]): Code[PInt64] = { coerce[PInt64](coerce[Long](a) * coerce[Long](b)) } + + def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]) = + Region.storeLong(dstAddress, Region.loadLong(srcAddress)) + + def storeShallowAtOffset(dstAddress: Long, srcAddress: Long) = + Region.storeLong(dstAddress, Region.loadLong(srcAddress)) + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long = + srcAddress + + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] = + srcAddress } object PInt64 { diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PStream.scala b/hail/src/main/scala/is/hail/expr/types/physical/PStream.scala index 5c96e02d0d8..937f62a9904 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PStream.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PStream.scala @@ -1,6 +1,7 @@ package is.hail.expr.types.physical -import is.hail.annotations.CodeOrdering +import is.hail.annotations.{CodeOrdering, Region} +import is.hail.asm4s.{Code, MethodBuilder} import is.hail.expr.ir.EmitMethodBuilder import is.hail.expr.types.virtual.TStream @@ -34,5 +35,17 @@ final case class PStream(elementType: PType, override val required: Boolean = fa def codeOrdering(mb: EmitMethodBuilder, other: PType): CodeOrdering = throw new UnsupportedOperationException("Stream comparison is currently undefined.") + + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean) = + throw new UnsupportedOperationException("Stream copyFromType is currently undefined") + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean) = + throw new UnsupportedOperationException("Stream copyFromType is currently undefined") + + def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]): Code[Unit] = + throw new UnsupportedOperationException("Stream storeShallowAtOffset is currently undefined") + + def storeShallowAtOffset(dstAddress: Long, srcAddress: Long) = + throw new UnsupportedOperationException("Stream storeShallowAtOffset is currently undefined") } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PStruct.scala b/hail/src/main/scala/is/hail/expr/types/physical/PStruct.scala index 00a6866f94e..a82f0807513 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PStruct.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PStruct.scala @@ -69,17 +69,17 @@ abstract class PStruct extends PBaseStruct { def loadField(region: Code[Region], offset: Code[Long], fieldName: String): Code[Long] - def loadField(offset: Code[Long], field: String): Code[Long] + def loadField(offset: Code[Long], fieldName: String): Code[Long] - final def isFieldDefined(offset: Code[Long], field: String): Code[Boolean] = !isFieldMissing(offset, field) + final def isFieldDefined(offset: Code[Long], fieldName: String): Code[Boolean] = !isFieldMissing(offset, fieldName) - def isFieldMissing(offset: Code[Long], field: String): Code[Boolean] + def isFieldMissing(offset: Code[Long], fieldName: String): Code[Boolean] def fieldOffset(offset: Code[Long], fieldName: String): Code[Long] - def setFieldPresent(offset: Code[Long], field: String): Code[Unit] + def setFieldPresent(offset: Code[Long], fieldName: String): Code[Unit] - def setFieldMissing(offset: Code[Long], field: String): Code[Unit] + def setFieldMissing(offset: Code[Long], fieldName: String): Code[Unit] def insertFields(fieldsToInsert: TraversableOnce[(String, PType)]): PStruct } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PType.scala b/hail/src/main/scala/is/hail/expr/types/physical/PType.scala index 620d698f4f6..f9c87f89d33 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PType.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PType.scala @@ -1,6 +1,7 @@ package is.hail.expr.types.physical import is.hail.annotations._ +import is.hail.asm4s._ import is.hail.check.{Arbitrary, Gen} import is.hail.expr.ir.{EmitMethodBuilder, IRParser, SortOrder} import is.hail.expr.types.virtual._ @@ -166,7 +167,7 @@ abstract class PType extends Serializable with Requiredness { def unsafeOrdering(): UnsafeOrdering = ??? - def isCanonical: Boolean = PType.canonical(this) == this // will recons, may need to rewrite this method + def isCanonical: Boolean = PType.canonical(this) == this // will recons, may need to rewrite this method def unsafeOrdering(rightType: PType): UnsafeOrdering = { require(this.isOfType(rightType)) @@ -281,7 +282,7 @@ abstract class PType extends Serializable with Requiredness { def deepInnerRequired(required: Boolean): PType = this match { - case t: PArray => PArray(t.elementType.deepInnerRequired(true), required) + case t: PArray => PArray(t.elementType.deepInnerRequired(true), required) case t: PSet => PSet(t.elementType.deepInnerRequired(true), required) case t: PDict => PDict(t.keyType.deepInnerRequired(true), t.valueType.deepInnerRequired(true), required) case t: PStruct => @@ -293,4 +294,19 @@ abstract class PType extends Serializable with Requiredness { case t => t.setRequired(required) } + + // Semantics: must be callable without requiredeness check: srcAddress must point to non-null value + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean): Code[Long] + + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long]): Code[Long] = + this.copyFromType(mb, region, srcPType, srcAddress, false) + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean): Long + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long): Long = + this.copyFromType(region, srcPType, srcAddress, false) + + def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]): Code[Unit] + + def storeShallowAtOffset(dstAddress: Long, srcAddress: Long) } diff --git a/hail/src/main/scala/is/hail/expr/types/physical/PVoid.scala b/hail/src/main/scala/is/hail/expr/types/physical/PVoid.scala index 0208f421de9..1a740c401b7 100644 --- a/hail/src/main/scala/is/hail/expr/types/physical/PVoid.scala +++ b/hail/src/main/scala/is/hail/expr/types/physical/PVoid.scala @@ -1,5 +1,6 @@ package is.hail.expr.types.physical -import is.hail.annotations.{CodeOrdering, ExtendedOrdering} +import is.hail.annotations.{CodeOrdering, Region} +import is.hail.asm4s.{Code, MethodBuilder} import is.hail.expr.ir.EmitMethodBuilder import is.hail.expr.types.virtual.{TVoid, Type} @@ -13,4 +14,16 @@ case object PVoid extends PType { override def _pretty(sb: StringBuilder, indent: Int, compact: Boolean): Unit = sb.append("PVoid") def codeOrdering(mb: EmitMethodBuilder, other: PType): CodeOrdering = null + + def copyFromType(mb: MethodBuilder, region: Code[Region], srcPType: PType, srcAddress: Code[Long], forceDeep: Boolean) = + throw new UnsupportedOperationException("PVoid copyFromType is currently undefined") + + def copyFromType(region: Region, srcPType: PType, srcAddress: Long, forceDeep: Boolean) = + throw new UnsupportedOperationException("PVoid copyFromType is currently undefined") + + def storeShallowAtOffset(dstAddress: Code[Long], srcAddress: Code[Long]): Code[Unit] = + throw new UnsupportedOperationException("PVoid storeShallowAtOffset is currently undefined") + + def storeShallowAtOffset(dstAddress: Long, srcAddress: Long) = + throw new UnsupportedOperationException("PVoid storeShallowAtOffset is currently undefined") } diff --git a/hail/src/test/scala/is/hail/expr/types/physical/PBaseStructSuite.scala b/hail/src/test/scala/is/hail/expr/types/physical/PBaseStructSuite.scala new file mode 100644 index 00000000000..98208c89b55 --- /dev/null +++ b/hail/src/test/scala/is/hail/expr/types/physical/PBaseStructSuite.scala @@ -0,0 +1,82 @@ +package is.hail.expr.types.physical + +import is.hail.HailSuite +import is.hail.annotations.Annotation +import org.testng.annotations.Test + +class PBaseStructSuite extends HailSuite { + @Test def testStructCopy() { + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PStruct(), PStruct(), Annotation(), + forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PStruct("a" -> PInt64()), PStruct("a" -> PInt64()), Annotation(12L), + forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PStruct("a" -> PInt64(true)), PStruct("a" -> PInt64(true)), Annotation(11L), + forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PStruct("a" -> PInt64(false)), PStruct("a" -> PInt64(true)), Annotation(3L), + expectCompileErr = true, forceDeep = forceDeep) + + var srcType = PStruct("a" -> PInt64(true), "b" -> PInt32(true), "c" -> PFloat64(true), "d" -> PFloat32(true), "e" -> PBoolean(true)) + var destType = PStruct("a" -> PInt64(true), "b" -> PInt32(true), "c" -> PFloat64(true), "d" -> PFloat32(true), "e" -> PBoolean(true)) + var expectedVal = Annotation(13L, 12, 13.0, 10.0F, true) + + PhysicalTestUtils.copyTestExecutor(srcType, destType, expectedVal, forceDeep = forceDeep) + + srcType = PStruct("a" -> srcType, "c" -> PFloat32()) + destType = PStruct("a" -> destType, "c" -> PFloat32()) + var nestedExpectedVal = Annotation(expectedVal, 13.0F) + PhysicalTestUtils.copyTestExecutor(srcType, destType, nestedExpectedVal, forceDeep = forceDeep) + + srcType = PStruct("a" -> PInt64(), "b" -> PInt32(), "c" -> PFloat64(), "d" -> PFloat32(), "e" -> PBoolean()) + destType = PStruct("a" -> PInt64(), "b" -> PInt32(), "c" -> PFloat64(), "d" -> PFloat32(), "e" -> PBoolean()) + PhysicalTestUtils.copyTestExecutor(srcType, destType, expectedVal, forceDeep = forceDeep) + + srcType = PStruct("a" -> srcType, "b" -> PFloat32()) + destType = PStruct("a" -> destType, "b" -> PFloat32()) + nestedExpectedVal = Annotation(expectedVal, 14.0F) + PhysicalTestUtils.copyTestExecutor(srcType, destType, nestedExpectedVal, forceDeep = forceDeep) + + srcType = PStruct("a" -> PInt64(), "b" -> PInt32(true), "c" -> PFloat64(), "d" -> PFloat32(), "e" -> PBoolean()) + destType = PStruct("a" -> PInt64(), "b" -> PInt32(), "c" -> PFloat64(), "d" -> PFloat32(), "e" -> PBoolean()) + PhysicalTestUtils.copyTestExecutor(srcType, destType, expectedVal, forceDeep = forceDeep) + + srcType = PStruct("a" -> srcType, "b" -> PFloat32()) + destType = PStruct("a" -> destType, "b" -> PFloat32()) + nestedExpectedVal = Annotation(expectedVal, 15.0F) + PhysicalTestUtils.copyTestExecutor(srcType, destType, nestedExpectedVal, forceDeep = forceDeep) + + srcType = PStruct("a" -> PInt64(), "b" -> PInt32(true), "c" -> PFloat64(), "d" -> PFloat32(), "e" -> PBoolean()) + destType = PStruct("a" -> PInt64(), "b" -> PInt32(), "c" -> PFloat64(true), "d" -> PFloat32(), "e" -> PBoolean()) + PhysicalTestUtils.copyTestExecutor(srcType, destType, expectedVal, expectCompileErr = true, forceDeep = forceDeep) + + srcType = PStruct("a" -> srcType, "b" -> PFloat32()) + destType = PStruct("a" -> destType, "b" -> PFloat32()) + nestedExpectedVal = Annotation(expectedVal, 13F) + PhysicalTestUtils.copyTestExecutor(srcType, destType, nestedExpectedVal, expectCompileErr = true, forceDeep = forceDeep) + + srcType = PStruct("a" -> PArray(PInt32(true)), "b" -> PInt64()) + destType = PStruct("a" -> PArray(PInt32()), "b" -> PInt64()) + expectedVal = Annotation(IndexedSeq(1,5,7,2,31415926), 31415926535897L) + PhysicalTestUtils.copyTestExecutor(srcType, destType, expectedVal, forceDeep = forceDeep) + + srcType = PStruct("a" -> PArray(PArray(PStruct("a" -> PInt32(true)))), "b" -> PInt64()) + destType = PStruct("a" -> PArray(PArray(PStruct("a" -> PInt32(true)))), "b" -> PInt64()) + expectedVal = Annotation(IndexedSeq(null, IndexedSeq(null, Annotation(1))), 31415926535897L) + PhysicalTestUtils.copyTestExecutor(srcType, destType, expectedVal, forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } + + @Test def tupleCopyTests() { + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PTuple(PString(true), PString(true)), PTuple(PString(), PString()), Annotation("1", "2"), + forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } +} diff --git a/hail/src/test/scala/is/hail/expr/types/physical/PBinarySuite.scala b/hail/src/test/scala/is/hail/expr/types/physical/PBinarySuite.scala new file mode 100644 index 00000000000..04bdc731c3d --- /dev/null +++ b/hail/src/test/scala/is/hail/expr/types/physical/PBinarySuite.scala @@ -0,0 +1,23 @@ +package is.hail.expr.types.physical + +import is.hail.HailSuite +import is.hail.annotations.Annotation +import org.testng.annotations.Test + +class PBinarySuite extends HailSuite { + @Test def testCopy() { + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PString(), PString(), "", + forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PString(), PString(true), "TopLevelDowncastAllowed", + forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PString(true), PString(), "UpcastAllowed", + forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } +} diff --git a/hail/src/test/scala/is/hail/expr/types/physical/PCallSuite.scala b/hail/src/test/scala/is/hail/expr/types/physical/PCallSuite.scala new file mode 100644 index 00000000000..c4456277315 --- /dev/null +++ b/hail/src/test/scala/is/hail/expr/types/physical/PCallSuite.scala @@ -0,0 +1,32 @@ +package is.hail.expr.types.physical + +import is.hail.HailSuite +import is.hail.annotations.{Annotation, Region, ScalaToRegionValue} +import is.hail.asm4s._ +import is.hail.expr.ir.EmitFunctionBuilder +import is.hail.utils._ +import org.testng.annotations.Test + +class PCallSuite extends HailSuite { + @Test def copyTests() { + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PCanonicalCall(), PCanonicalCall(), + 2, + forceDeep = forceDeep) + + // downcast at top level allowed, since PCanonicalCall wraps a primitive + PhysicalTestUtils.copyTestExecutor(PCanonicalCall(), PCanonicalCall(true), + 2, + forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PArray(PCanonicalCall(true), true), PArray(PCanonicalCall()), + IndexedSeq(2, 3), forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PArray(PCanonicalCall(), true), PArray(PCanonicalCall(true)), + IndexedSeq(2, 3), expectCompileErr = true, forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } +} diff --git a/hail/src/test/scala/is/hail/expr/types/physical/PContainerTest.scala b/hail/src/test/scala/is/hail/expr/types/physical/PContainerTest.scala index 255c8f61d1f..ba22a054d1d 100644 --- a/hail/src/test/scala/is/hail/expr/types/physical/PContainerTest.scala +++ b/hail/src/test/scala/is/hail/expr/types/physical/PContainerTest.scala @@ -1,9 +1,9 @@ package is.hail.expr.types.physical import is.hail.HailSuite -import is.hail.annotations.{Region, ScalaToRegionValue} +import is.hail.annotations.{Annotation, Region, ScalaToRegionValue} import is.hail.asm4s._ -import is.hail.expr.ir.{EmitFunctionBuilder} +import is.hail.expr.ir.EmitFunctionBuilder import is.hail.utils._ import org.testng.annotations.Test @@ -173,4 +173,109 @@ class PContainerTest extends HailSuite { testConvert(sourceType, destType, nullInByte(79, 72), true) testConvert(sourceType, destType, nullInByte(79, 8), true) } -} \ No newline at end of file + + @Test def arrayCopyTest() { + // Note: can't test where data is null due to ArrayStack.top semantics (ScalaToRegionValue: assert(size_ > 0)) + + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PArray(PInt32()), PArray(PInt64()), IndexedSeq(1, 2, 3, 4, 5, 6, 7, 8, 9), + expectCompileErr = true, forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PArray(PInt32()), PArray(PInt32()), IndexedSeq(1, 2, 3, 4), + forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PInt32()), PArray(PInt32()), IndexedSeq(1, 2, 3, 4), + forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PInt32()), PArray(PInt32()), IndexedSeq(1, null, 3, 4), + forceDeep = forceDeep) + + // test upcast + PhysicalTestUtils.copyTestExecutor(PArray(PInt32(true)), PArray(PInt32()), IndexedSeq(1, 2, 3, 4), + forceDeep = forceDeep) + + // test mismatched top-level requiredeness, allowed because by source value address must be present and therefore non-null + PhysicalTestUtils.copyTestExecutor(PArray(PInt32()), PArray(PInt32(), true), IndexedSeq(1, 2, 3, 4), + forceDeep = forceDeep) + + // downcast disallowed + PhysicalTestUtils.copyTestExecutor(PArray(PInt32()), PArray(PInt32(true)), IndexedSeq(1, 2, 3, 4), + expectCompileErr = true, forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PInt64())), PArray(PArray(PInt64(), true)), + FastIndexedSeq(FastIndexedSeq(20L), FastIndexedSeq(1L), FastIndexedSeq(20L,5L,31L,41L), FastIndexedSeq(1L,2L,3L)), + expectCompileErr = true, forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PInt64())), PArray(PArray(PInt64(), true)), + FastIndexedSeq(FastIndexedSeq(20L), FastIndexedSeq(1L), FastIndexedSeq(20L,5L,31L,41L), FastIndexedSeq(1L,2L,3L)), + expectCompileErr = true, forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PInt64())), PArray(PArray(PInt64(true))), + FastIndexedSeq(FastIndexedSeq(20L), FastIndexedSeq(1L), FastIndexedSeq(20L,5L,31L,41L), FastIndexedSeq(1L,2L,3L)), + expectCompileErr = true, forceDeep = forceDeep) + + // test empty arrays + PhysicalTestUtils.copyTestExecutor(PArray(PInt32()), PArray(PInt32()), FastIndexedSeq(), + forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PInt32(true)), PArray(PInt32(true)), FastIndexedSeq(), + forceDeep = forceDeep) + + // test missing-only array + PhysicalTestUtils.copyTestExecutor(PArray(PInt64()), PArray(PInt64()), + FastIndexedSeq(null), forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PInt64())), PArray(PArray(PInt64())), + FastIndexedSeq(FastIndexedSeq(null)), forceDeep = forceDeep) + + // test 2D arrays + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PInt64())), PArray(PArray(PInt64())), + FastIndexedSeq(null, FastIndexedSeq(null), FastIndexedSeq(20L,5L,31L,41L), FastIndexedSeq(1L,2L,3L)), + forceDeep = forceDeep) + + // test complex nesting + val complexNesting = FastIndexedSeq( + FastIndexedSeq( FastIndexedSeq(20L,30L,31L,41L), FastIndexedSeq(20L,22L,31L,43L) ), + FastIndexedSeq( FastIndexedSeq(1L,3L,31L,41L), FastIndexedSeq(0L,30L,17L,41L) ) + ) + + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PArray(PInt64(true), true), true), true), PArray(PArray(PArray(PInt64()))), + complexNesting, forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PArray(PInt64(true), true), true)), PArray(PArray(PArray(PInt64()))), + complexNesting, forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PArray(PInt64(true), true))), PArray(PArray(PArray(PInt64()))), + complexNesting, forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PArray(PInt64(true)))), PArray(PArray(PArray(PInt64()))), + complexNesting, forceDeep = forceDeep) + PhysicalTestUtils.copyTestExecutor(PArray(PArray(PArray(PInt64()))), PArray(PArray(PArray(PInt64()))), + complexNesting, forceDeep = forceDeep) + + val srcType = PArray(PStruct("a" -> PArray(PInt32(true)), "b" -> PInt64())) + val destType = PArray(PStruct("a" -> PArray(PInt32()), "b" -> PInt64())) + val expectedVal = IndexedSeq(Annotation(IndexedSeq(1,5,7,2,31415926), 31415926535897L)) + PhysicalTestUtils.copyTestExecutor(srcType, destType, expectedVal, forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } + + @Test def dictCopyTests() { + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PDict(PString(), PInt32()), PDict(PString(), PInt32()), Map("test" -> 1), + forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PDict(PString(true), PInt32(true)), PDict(PString(), PInt32()), Map("test2" -> 2), + forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PDict(PString(), PInt32()), PDict(PString(true), PInt32()), Map("test3" -> 3), + expectCompileErr = true, forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } + + @Test def setCopyTests() { + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PSet(PString(true)), PSet(PString()), Set("1", "2"), + forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } +} diff --git a/hail/src/test/scala/is/hail/expr/types/physical/PIntervalSuite.scala b/hail/src/test/scala/is/hail/expr/types/physical/PIntervalSuite.scala new file mode 100644 index 00000000000..d74291f0634 --- /dev/null +++ b/hail/src/test/scala/is/hail/expr/types/physical/PIntervalSuite.scala @@ -0,0 +1,33 @@ +package is.hail.expr.types.physical + +import is.hail.HailSuite +import is.hail.annotations.{Annotation, Region, ScalaToRegionValue} +import is.hail.asm4s._ +import is.hail.expr.ir.EmitFunctionBuilder +import is.hail.utils._ +import org.testng.annotations.Test + +class PIntervalSuite extends HailSuite { + @Test def copyTests() { + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PCanonicalInterval(PInt64()), PCanonicalInterval(PInt64()), + Interval(IntervalEndpoint(1000L, 1), IntervalEndpoint(1000L, 1)), + forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PCanonicalInterval(PInt64(true)), PCanonicalInterval(PInt64()), + Interval(IntervalEndpoint(1000L, 1), IntervalEndpoint(1000L, 1)), + forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PCanonicalInterval(PInt64()), PCanonicalInterval(PInt64(true)), + Interval(IntervalEndpoint(1000L, 1), IntervalEndpoint(1000L, 1)), + expectCompileErr = true, forceDeep = forceDeep) + + PhysicalTestUtils.copyTestExecutor(PCanonicalInterval(PInt64(true)), PCanonicalInterval(PInt64(true)), + Interval(IntervalEndpoint(1000L, 1), IntervalEndpoint(1000L, 1)), + forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } +} diff --git a/hail/src/test/scala/is/hail/expr/types/physical/PNDArraySuite.scala b/hail/src/test/scala/is/hail/expr/types/physical/PNDArraySuite.scala new file mode 100644 index 00000000000..e5c6e4c266a --- /dev/null +++ b/hail/src/test/scala/is/hail/expr/types/physical/PNDArraySuite.scala @@ -0,0 +1,20 @@ +package is.hail.expr.types.physical + +import is.hail.HailSuite +import is.hail.annotations.{Annotation, Region, ScalaToRegionValue} +import is.hail.asm4s._ +import is.hail.expr.ir.EmitFunctionBuilder +import is.hail.utils._ +import org.testng.annotations.Test + +class PNDArraySuite extends HailSuite { + @Test def copyTests() { + def runTests(forceDeep: Boolean) { + PhysicalTestUtils.copyTestExecutor(PNDArray(PInt64(true), 1), PNDArray(PInt64(true), 1), Annotation(0, 1, Annotation(1L), Annotation(1L), IndexedSeq(4L,5L,6L)), + forceDeep = forceDeep) + } + + runTests(true) + runTests(false) + } +} diff --git a/hail/src/test/scala/is/hail/expr/types/physical/PhysicalTestUtils.scala b/hail/src/test/scala/is/hail/expr/types/physical/PhysicalTestUtils.scala new file mode 100644 index 00000000000..7d71d274437 --- /dev/null +++ b/hail/src/test/scala/is/hail/expr/types/physical/PhysicalTestUtils.scala @@ -0,0 +1,49 @@ +package is.hail.expr.types.physical +import is.hail.utils.log +import is.hail.annotations.{Region, SafeIndexedSeq, SafeRow, ScalaToRegionValue, UnsafeRow} +import is.hail.expr.ir.EmitFunctionBuilder + +object PhysicalTestUtils { + def copyTestExecutor(sourceType: PType, destType: PType, sourceValue: Any, + expectCompileErr: Boolean = false, expectRuntimeErr: Boolean = false, forceDeep: Boolean = false) { + val region = Region() + val srcRegion = Region() + + val srcOffset = ScalaToRegionValue(srcRegion, sourceType, sourceValue) + + val fb = EmitFunctionBuilder[Region, Long, Long]("not_empty") + val codeRegion = fb.getArg[Region](1).load() + val value = fb.getArg[Long](2) + var compileSuccess = false + try { + fb.emit(destType.copyFromType(fb.apply_method, codeRegion, sourceType, value, forceDeep = forceDeep)) + compileSuccess = true + } catch { + case e: Throwable => { + region.clear() + srcRegion.clear() + if(expectCompileErr) { + log.info("Caught expected compile-time error") + return assert(true) + } + + throw new Error(e) + } + } + + if(compileSuccess && expectCompileErr) { + region.clear() + srcRegion.clear() + throw new Error("Did not receive expected compile time error") + } + + val f = fb.result()() + val copyOff = f(region, srcOffset) + val copy = UnsafeRow.read(destType, region, copyOff) + + log.info(s"Copied value: ${copy}, Source value: ${sourceValue}") + assert(copy == sourceValue) + region.clear() + srcRegion.clear() + } +} \ No newline at end of file