Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Array[Int] instead of Array[Array[Boolean]] for [class,trait]_has_trait #3279

Merged
merged 11 commits into from
May 29, 2023
26 changes: 26 additions & 0 deletions tools/src/main/scala/scala/scalanative/codegen/BitMatrix.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package scala.scalanative

private[scalanative] class BitMatrix private (
Copy link
Contributor Author

@lolgab lolgab May 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a minimal version of our java BitSet implementation with fixed size and no bound checks.
It represents a matrix so it calculates the array index storing the matrix by rows.

bits: Array[Long],
columns: Int
) {
import BitMatrix.{AddressBitsPerWord, ElementSize, RightBits}

def set(row: Int, col: Int): Unit = {
val bitIndex = row * columns + col
bits(bitIndex >> AddressBitsPerWord) |= 1L << (bitIndex & RightBits)
}

def toSeq = bits.toSeq
}
private[scalanative] object BitMatrix {
private[scalanative] final val AddressBitsPerWord = 6 // Int Based 2^6 = 64
private[scalanative] final val ElementSize = 1 << AddressBitsPerWord
private[scalanative] final val RightBits = ElementSize - 1

def apply(rows: Int, columns: Int): BitMatrix = {
val nbits = rows * columns
val length = (nbits + RightBits) >> AddressBitsPerWord
new BitMatrix(new Array[Long](length), columns)
}
}
164 changes: 126 additions & 38 deletions tools/src/main/scala/scala/scalanative/codegen/Generate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,142 @@ object Generate {
}

def genClassHasTrait(): Unit = {
genHasTrait(
ClassHasTraitName,
ClassHasTraitSig,
meta.hasTraitTables.classHasTraitTy,
meta.hasTraitTables.classHasTraitVal
)
}

def genTraitHasTrait(): Unit = {
genHasTrait(
TraitHasTraitName,
TraitHasTraitSig,
meta.hasTraitTables.traitHasTraitTy,
meta.hasTraitTables.traitHasTraitVal
)
}

// def __get_[class,trait]_has_trait(firstid: Int, secondid: Int): Boolean = {
// val columns = meta.traits.length
// val row = firstid * columns
// val bitIndex = row + secondid
// val arrayPos = bitIndex >> AddressBitsPerWord
// val long = bits(arrayPos)
// val toShift = bitIndex & RightBits
// val toShiftLong = toShift.toLong
// val mask = 1L << toShiftLong
// val and = long & mask
// val result = and != 0L
//
// result
// }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we need this comment. I can remove it if you think it's redundant.

private def genHasTrait(
name: Global.Member,
sig: Type.Function,
tableTy: Type,
tableVal: Val
): Unit = {
implicit val fresh = Fresh()
val classid, traitid = Val.Local(fresh(), Type.Int)
val boolptr = Val.Local(fresh(), Type.Ptr)
val firstid, secondid = Val.Local(fresh(), Type.Int)
val row = Val.Local(fresh(), Type.Int)
val columns = Val.Int(meta.traits.length)
val bitIndex = Val.Local(fresh(), Type.Int)
val arrayPos = Val.Local(fresh(), Type.Int)
val longptr = Val.Local(fresh(), Type.Ptr)
val long = Val.Local(fresh(), Type.Long)
val toShift = Val.Local(fresh(), Type.Int)
val toShiftLong = Val.Local(fresh(), Type.Long)
val mask = Val.Local(fresh(), Type.Long)
val and = Val.Local(fresh(), Type.Long)
val result = Val.Local(fresh(), Type.Bool)

buf += Defn.Define(
Attrs(inlineHint = Attr.AlwaysInline),
ClassHasTraitName,
ClassHasTraitSig,
name,
sig,
Seq(
Inst.Label(fresh(), Seq(classid, traitid)),
Inst.Label(fresh(), Seq(firstid, secondid)),
Inst.Let(
row.name,
Op.Bin(Bin.Imul, Type.Int, firstid, columns),
Next.None
),
Inst.Let(
boolptr.name,
bitIndex.name,
Op.Bin(Bin.Iadd, Type.Int, row, secondid),
Next.None
),
Inst.Let(
arrayPos.name,
Op.Bin(
Bin.Ashr,
Type.Int,
bitIndex,
Val.Int(BitMatrix.AddressBitsPerWord)
),
Next.None
),
Inst.Let(
longptr.name,
Op.Elem(
meta.hasTraitTables.classHasTraitTy,
meta.hasTraitTables.classHasTraitVal,
Seq(Val.Int(0), classid, traitid)
tableTy,
tableVal,
Seq(Val.Int(0), arrayPos)
),
Next.None
),
Inst.Let(long.name, Op.Load(Type.Long, longptr), Next.None),
Inst.Let(
toShift.name,
Op.Bin(
Bin.And,
Type.Int,
bitIndex,
Val.Int(BitMatrix.RightBits)
),
Next.None
),
Inst.Let(
toShiftLong.name,
Op.Conv(
Conv.Sext,
Type.Long,
toShift
),
Next.None
),
Inst.Let(
mask.name,
Op.Bin(
Bin.Shl,
Type.Long,
Val.Long(1),
toShiftLong
),
Next.None
),
Inst.Let(
and.name,
Op.Bin(
Bin.And,
Type.Long,
long,
mask
),
Next.None
),
Inst.Let(
result.name,
Op.Comp(
Comp.Ine,
Type.Long,
and,
Val.Long(0)
),
Next.None
),
Inst.Let(result.name, Op.Load(Type.Bool, boolptr), Next.None),
Inst.Ret(result)
)
)
Expand All @@ -100,33 +215,6 @@ object Generate {
}
}

def genTraitHasTrait(): Unit = {
implicit val fresh = Fresh()
val leftid, rightid = Val.Local(fresh(), Type.Int)
val boolptr = Val.Local(fresh(), Type.Ptr)
val result = Val.Local(fresh(), Type.Bool)

buf += Defn.Define(
Attrs(inlineHint = Attr.AlwaysInline),
TraitHasTraitName,
TraitHasTraitSig,
Seq(
Inst.Label(fresh(), Seq(leftid, rightid)),
Inst.Let(
boolptr.name,
Op.Elem(
meta.hasTraitTables.traitHasTraitTy,
meta.hasTraitTables.traitHasTraitVal,
Seq(Val.Int(0), leftid, rightid)
),
Next.None
),
Inst.Let(result.name, Op.Load(Type.Bool, boolptr), Next.None),
Inst.Ret(result)
)
)
}

/* Generate set of instructions using common exception handling, generate method
* would return 0 if would execute successfully exception and 1 in otherwise */
private def withExceptionHandler(
Expand Down Expand Up @@ -504,7 +592,7 @@ object Generate {
}
}

private object Impl {
object Impl {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to move the constats that I need in to use in Lower outside of the Impl object and make them public.

val rttiModule = Global.Top("java.lang.rtti$")

val ClassHasTraitName =
Expand Down
57 changes: 29 additions & 28 deletions tools/src/main/scala/scala/scalanative/codegen/HasTraitTables.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,57 @@ import scalanative.linker.{Trait, Class}
class HasTraitTables(meta: Metadata) {
private implicit val pos: Position = Position.NoPosition

val classHasTraitName = Global.Top("__class_has_trait")
private val classHasTraitName = Global.Top("__class_has_trait")
val classHasTraitVal = Val.Global(classHasTraitName, Type.Ptr)
var classHasTraitTy: Type = _
var classHasTraitDefn: Defn = _

val traitHasTraitName = Global.Top("__trait_has_trait")
private val traitHasTraitName = Global.Top("__trait_has_trait")
val traitHasTraitVal = Val.Global(traitHasTraitName, Type.Ptr)
var traitHasTraitTy: Type = _
var traitHasTraitDefn: Defn = _

initClassHasTrait()
initTraitHasTrait()

def markTraits(row: Array[Boolean], cls: Class): Unit = {
cls.traits.foreach(markTraits(row, _))
cls.parent.foreach(markTraits(row, _))
private def markTraits(matrix: BitMatrix, row: Int, cls: Class): Unit = {
cls.traits.foreach(markTraits(matrix, row, _))
cls.parent.foreach(markTraits(matrix, row, _))
}

def markTraits(row: Array[Boolean], trt: Trait): Unit = {
row(meta.ids(trt)) = true
trt.traits.foreach { right => row(meta.ids(right)) = true }
trt.traits.foreach(markTraits(row, _))
private def markTraits(matrix: BitMatrix, row: Int, trt: Trait): Unit = {
matrix.set(row, meta.ids(trt))
trt.traits.foreach(markTraits(matrix, row, _))
}

def initClassHasTrait(): Unit = {
val columns = meta.classes.map { cls =>
val row = new Array[Boolean](meta.traits.length)
markTraits(row, cls)
Val.ArrayValue(Type.Bool, row.toSeq.map(Val.Bool))
private def initClassHasTrait(): Unit = {
val matrix = BitMatrix(meta.classes.length, meta.traits.length)
var row = 0
meta.classes.foreach { cls =>
markTraits(matrix, row, cls)

row += 1
}
val table =
Val.ArrayValue(Type.ArrayValue(Type.Bool, meta.traits.length), columns)
val tableVal = Val.ArrayValue(Type.Long, matrix.toSeq.map(Val.Long))

classHasTraitTy = table.ty
classHasTraitDefn =
Defn.Const(Attrs.None, classHasTraitName, table.ty, table)
Defn.Const(Attrs.None, classHasTraitName, tableVal.ty, tableVal)
classHasTraitTy = tableVal.ty
}

def initTraitHasTrait(): Unit = {
val columns = meta.traits.map { left =>
val row = new Array[Boolean](meta.traits.length)
markTraits(row, left)
row(meta.ids(left)) = true
Val.ArrayValue(Type.Bool, row.toSeq.map(Val.Bool))
private def initTraitHasTrait(): Unit = {
val matrix = BitMatrix(meta.traits.length, meta.traits.length)
var row = 0
meta.traits.foreach { left =>
markTraits(matrix, row, left)
matrix.set(row, meta.ids(left))

row += 1
}
val table =
Val.ArrayValue(Type.ArrayValue(Type.Bool, meta.traits.length), columns)
val tableVal = Val.ArrayValue(Type.Long, matrix.toSeq.map(Val.Long))

traitHasTraitTy = table.ty
traitHasTraitDefn =
Defn.Const(Attrs.None, traitHasTraitName, table.ty, table)
Defn.Const(Attrs.None, traitHasTraitName, tableVal.ty, tableVal)
traitHasTraitTy = tableVal.ty
}
}
12 changes: 5 additions & 7 deletions tools/src/main/scala/scala/scalanative/codegen/Lower.scala
Original file line number Diff line number Diff line change
Expand Up @@ -950,16 +950,14 @@ object Lower {
unwind
)
val id = let(Op.Load(Type.Int, idptr), unwind)
val boolptr = let(
Op.Elem(
hasTraitTables.classHasTraitTy,
hasTraitTables.classHasTraitVal,
Seq(zero, id, Val.Int(meta.ids(trt)))
let(
Op.Call(
Generate.Impl.ClassHasTraitSig,
Val.Global(Generate.Impl.ClassHasTraitName, Generate.Impl.ClassHasTraitSig),
Seq(id, Val.Int(meta.ids(trt)))
),
unwind
)
let(Op.Load(Type.Bool, boolptr), unwind)

case _ =>
util.unsupported(s"is[$ty] $obj")
}
Expand Down