scala.scalanative.nir.Vals.scala Maven / Gradle / Ivy
package scala.scalanative
package nir
import java.lang.Float.floatToRawIntBits
import java.lang.Double.doubleToRawLongBits
/** A NIR value. */
sealed abstract class Val {
/** The type of the value. */
final def ty: Type = this match {
case Val.Null =>
Type.Null
case Val.Zero(ty) =>
ty
case Val.True | Val.False =>
Type.Bool
case Val.Size(_) =>
Type.Size
case Val.Char(_) =>
Type.Char
case Val.Byte(_) =>
Type.Byte
case Val.Short(_) =>
Type.Short
case Val.Int(_) =>
Type.Int
case Val.Long(_) =>
Type.Long
case Val.Float(_) =>
Type.Float
case Val.Double(_) =>
Type.Double
case Val.StructValue(vals) =>
Type.StructValue(vals.map(_.ty))
case Val.ArrayValue(ty, vals) =>
Type.ArrayValue(ty, vals.length)
case v: Val.ByteString =>
Type.ArrayValue(Type.Byte, v.byteCount)
case Val.Local(_, ty) =>
ty
case Val.Global(_, ty) =>
ty
case Val.Unit =>
Type.Unit
case Val.Const(_) =>
Type.Ptr
case Val.String(_) =>
Type.Ref(Rt.String.name, exact = true, nullable = false)
case Val.Virtual(_) =>
Type.Virtual
case Val.ClassOf(n) =>
Rt.Class
}
/** A textual representation of `this`. */
final def show: String = nir.Show(this)
/** `true` iff `this` represents a virtual value. */
final def isVirtual: Boolean =
this.isInstanceOf[Val.Virtual]
/** `true` iff `this` is in canonical form. */
final def isCanonical: Boolean = this match {
case Val.True | Val.False =>
true
case Val.Char(_) =>
true
case Val.Size(_) =>
true
case Val.Byte(_) | Val.Short(_) | Val.Int(_) | Val.Long(_) =>
true
case Val.Float(_) | Val.Double(_) =>
true
case Val.Global(_, _) | Val.Null =>
true
case _ =>
false
}
/** `true` iff `this` is the "zero" value of its type. */
final def isZero: Boolean = this match {
case Val.Zero(_) =>
true
case Val.False =>
true
case Val.Char('\u0000') =>
true
case Val.Size(0) =>
true
case Val.Byte(0) | Val.Short(0) | Val.Int(0) | Val.Long(0L) =>
true
case Val.Float(0f) | Val.Double(0d) =>
true
case Val.Null =>
true
case _ =>
false
}
/** `true` iff `this` is the value "1" in its type. */
final def isOne: Boolean = this match {
case Val.True =>
true
case Val.Char(c) if c.toInt == 1 =>
true
case Val.Size(1) =>
true
case Val.Byte(1) | Val.Short(1) | Val.Int(1) | Val.Long(1L) =>
true
case Val.Float(1f) | Val.Double(1d) =>
true
case _ =>
false
}
/** `true` iff `this` is a numerical value equal to "-1". */
final def isMinusOne: Boolean = this match {
case Val.Size(-1) =>
true
case Val.Byte(-1) | Val.Short(-1) | Val.Int(-1) | Val.Long(-1L) =>
true
case Val.Float(-1f) | Val.Double(-1d) =>
true
case _ =>
false
}
/** `true` iff `this` is the minimum value of a signed numerical type. */
final def isSignedMinValue(is32BitPlatform: Boolean): Boolean = this match {
case Val.Size(v) =>
if (is32BitPlatform) v == Int.MinValue else v == Long.MinValue
case Val.Byte(v) =>
v == Byte.MinValue
case Val.Short(v) =>
v == Short.MinValue
case Val.Int(v) =>
v == Int.MinValue
case Val.Long(v) =>
v == Long.MinValue
case _ =>
false
}
/** `true` iff `this` is the maximum value of a signed numerical type. */
final def isSignedMaxValue(is32BitPlatform: Boolean): Boolean = this match {
case Val.Size(v) =>
if (is32BitPlatform) v == Int.MaxValue else v == Long.MaxValue
case Val.Byte(v) =>
v == Byte.MaxValue
case Val.Short(v) =>
v == Short.MaxValue
case Val.Int(v) =>
v == Int.MaxValue
case Val.Long(v) =>
v == Long.MaxValue
case _ =>
false
}
/** `true` iff `this` is the minimum value of an unsigned numerical type. */
final def isUnsignedMinValue: Boolean =
isZero
/** `true` iff `this` is the maximum value of an unsigned numerical type. */
final def isUnsignedMaxValue: Boolean = this match {
case _ if isMinusOne =>
true
case Val.Char(c) =>
c == Char.MaxValue
case _ => false
}
/** The canonical form of `this` */
final def canonicalize: Val = this match {
case Val.Zero(Type.Bool) =>
Val.False
case Val.Zero(Type.Size) =>
Val.Size(0)
case Val.Zero(Type.Char) =>
Val.Char('\u0000')
case Val.Zero(Type.Byte) =>
Val.Byte(0.toByte)
case Val.Zero(Type.Short) =>
Val.Short(0.toShort)
case Val.Zero(Type.Int) =>
Val.Int(0)
case Val.Zero(Type.Long) =>
Val.Long(0L)
case Val.Zero(Type.Float) =>
Val.Float(0f)
case Val.Zero(Type.Double) =>
Val.Double(0d)
case Val.Zero(Type.Ptr) | Val.Zero(_: Type.RefKind) =>
Val.Null
case _ =>
this
}
/** `true` iff `this` is effectively a nullable value. */
def isNullable: Boolean = this match {
case _: Val.Global | _: Val.Local | _: Val.Zero =>
this.ty match {
case ref: nir.Type.Ref => ref.isNullable
case ty => nir.Type.isPtrType(ty)
}
case Val.Null => true
// Const is always a non-nullable pointer to possibly nullable data
case _: Val.Const => false
// All remaining data types can never be nullable
// Val.Struct and Val.ArrayValue are aggregate types, can never be null
// Val.ByteString is basically Val.ArrayValue
case _ => false
}
/** `true` iff `this` is effectively a literal value. */
def isLiteral: Boolean = this match {
case _: Val.Global | _: Val.Local | _: Val.Const => false
case v: Val.StructValue => v.values.forall(_.isLiteral)
case v: Val.ArrayValue => v.values.forall(_.isLiteral)
// All remaining value can be always treated as literals
// Val.Zero is effectively literal
// Val.ByteString is always a literal
case _ => true
}
}
object Val {
/** The constant Boolean 'true'. */
case object True extends Val
/** The constant Boolean 'false'. */
case object False extends Val
/** A Boolean constant. */
object Bool extends (Boolean => Val) {
def apply(value: Boolean): Val =
if (value) True else False
def unapply(value: Val): Option[Boolean] = value match {
case True => Some(true)
case False => Some(false)
case _ => scala.None
}
}
/** The constant 'null' value. */
case object Null extends Val
/** The "zero" value of the given NIR type. */
final case class Zero(of: nir.Type) extends Val
/** A numerical value suitable to represent the size of a container. */
final case class Size(value: scala.Long) extends Val
/** 16-bit unsigned Unicode character */
final case class Char(value: scala.Char) extends Val
/** A 8-bit signed two’s complement integer. */
final case class Byte(value: scala.Byte) extends Val
/** A 16-bit signed two’s complement integer. */
final case class Short(value: scala.Short) extends Val
/** A 32-bit signed two’s complement integer. */
final case class Int(value: scala.Int) extends Val
/** A 64-bit signed two’s complement integer. */
final case class Long(value: scala.Long) extends Val
/** A 32-bit IEEE 754 single-precision float. */
final case class Float(value: scala.Float) extends Val {
override def equals(that: Any): Boolean = that match {
case Float(thatValue) =>
val theseBits = floatToRawIntBits(value)
val thoseBits = floatToRawIntBits(thatValue)
theseBits == thoseBits
case _ => false
}
}
/** A 64-bit IEEE 754 double-precision float. */
final case class Double(value: scala.Double) extends Val {
override def equals(that: Any): Boolean = that match {
case Double(thatValue) =>
val theseBits = doubleToRawLongBits(value)
val thoseBits = doubleToRawLongBits(thatValue)
theseBits == thoseBits
case _ => false
}
}
/** A heterogeneous collection of data members. */
final case class StructValue(values: Seq[Val]) extends Val
/** A homogeneous collection of data members. */
final case class ArrayValue(elemty: nir.Type, values: Seq[Val]) extends Val
/** A collection of bytes.
*
* Unlike arrays, byte strings are implicitly null-terminated. Hence, they
* correspond to C-string literals. For example, `ByteString(Array(97))` will
* be compiled to `c"a\0"`.
*/
final case class ByteString(bytes: Array[scala.Byte]) extends Val {
def byteCount: scala.Int = bytes.length + 1
}
/** A local SSA variable. */
final case class Local(id: nir.Local, valty: nir.Type) extends Val
/** A reference to a global variable, constant, or method. */
final case class Global(name: nir.Global, valty: nir.Type) extends Val
/** The unit value. */
case object Unit extends Val
/** A constant.
*
* Note that this class does not behave like a literal constant, which are
* represented by `ByteString`, `Zero`, `Int`, etc. Instead, it represents a
* pointer to some constant value.
*/
final case class Const(value: Val) extends Val
/** A character string.
*
* Values of this type correspond to instances of `java.lang.String` and are
* compiled as global arrays of UTF-16 characters. Use `ByteString` to
* represent C-string literals.
*/
final case class String(value: java.lang.String) extends Val
/** A virtual value.
*
* Virtual values only serve as placeholders during optimization. They are
* not serializable and are never emitted by the compiler plugin.
*/
final case class Virtual(key: scala.Long) extends Val
/** A reference to `java.lang.Class[_]` of given symbol `name`.
*
* Instances are emitted as global variables during code feneration. They are
* used to deduplicate `Class` instances. There should be only 1 instance per
* type.
*
* Note that, althrough they are currently emitted as global variables,
* instances of this type could be constants. However, when we added
* multithreading and object monitors, we needed to edit one of its fields
* (specifically, `lockWord`), which contains an `ObjectMonitor` or a bit set
* of lock word.
*/
final case class ClassOf(name: nir.Global.Top) extends Val
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy