diff --git a/docs/contrib/nir.rst b/docs/contrib/nir.rst index 510c3ee0fe..acc15214bb 100644 --- a/docs/contrib/nir.rst +++ b/docs/contrib/nir.rst @@ -532,13 +532,14 @@ Boolean Corresponds to LLVM's ``true`` and ``false``. -Zero -```` +Zero and null +````````````` .. code-block:: text + null zero $type -Corresponds to LLVM's ``zeroinitializer``. +Corresponds to LLVM's ``null`` and ``zeroinitializer``. Integer ``````` diff --git a/nir/src/main/scala/scala/scalanative/nir/Show.scala b/nir/src/main/scala/scala/scalanative/nir/Show.scala index bf01feb4b1..6494da8bde 100644 --- a/nir/src/main/scala/scala/scalanative/nir/Show.scala +++ b/nir/src/main/scala/scala/scalanative/nir/Show.scala @@ -368,6 +368,8 @@ object Show { str("true") case Val.False => str("false") + case Val.Null => + str("null") case Val.Zero(ty) => str("zero[") type_(ty) diff --git a/nir/src/main/scala/scala/scalanative/nir/Vals.scala b/nir/src/main/scala/scala/scalanative/nir/Vals.scala index 51982aef25..d679ecb5ac 100644 --- a/nir/src/main/scala/scala/scalanative/nir/Vals.scala +++ b/nir/src/main/scala/scala/scalanative/nir/Vals.scala @@ -7,6 +7,7 @@ import java.lang.Double.doubleToRawLongBits sealed abstract class Val { final def ty: Type = this match { case Val.None => Type.None + case Val.Null => Type.Ptr case Val.Zero(ty) => ty case Val.Undef(ty) => ty case Val.True | Val.False => Type.Bool @@ -34,6 +35,7 @@ object Val { final case object None extends Val final case object True extends Val final case object False extends Val + final case object Null extends Val final case class Zero(of: nir.Type) extends Val final case class Undef(of: nir.Type) extends Val final case class Byte(value: scala.Byte) extends Val @@ -63,7 +65,6 @@ object Val { final case class Chars(value: java.lang.String) extends Val final case class Local(name: nir.Local, valty: nir.Type) extends Val final case class Global(name: nir.Global, valty: nir.Type) extends Val - val Null = Zero(Type.Ptr) def Bool(bool: Boolean) = if (bool) True else False // high-level diff --git a/nir/src/main/scala/scala/scalanative/nir/serialization/BinaryDeserializer.scala b/nir/src/main/scala/scala/scalanative/nir/serialization/BinaryDeserializer.scala index 31be8bf86b..bbe0588bbb 100644 --- a/nir/src/main/scala/scala/scalanative/nir/serialization/BinaryDeserializer.scala +++ b/nir/src/main/scala/scala/scalanative/nir/serialization/BinaryDeserializer.scala @@ -293,7 +293,7 @@ final class BinaryDeserializer(_buffer: => ByteBuffer) { case T.NoneVal => Val.None case T.TrueVal => Val.True case T.FalseVal => Val.False - case T.ZeroVal => Val.Zero(getType) + case T.ZeroVal => getZero() case T.UndefVal => Val.Undef(getType) case T.ByteVal => Val.Byte(get) case T.ShortVal => Val.Short(getShort) @@ -311,4 +311,11 @@ final class BinaryDeserializer(_buffer: => ByteBuffer) { case T.ConstVal => Val.Const(getVal) case T.StringVal => Val.String(getString) } + private def getZero(): Val = { + val ty = getType + ty match { + case Type.Ptr | _: Type.RefKind => Val.Null + case _ => Val.Zero(ty) + } + } } diff --git a/nir/src/main/scala/scala/scalanative/nir/serialization/BinarySerializer.scala b/nir/src/main/scala/scala/scalanative/nir/serialization/BinarySerializer.scala index fee636c327..1f3ff1c8c0 100644 --- a/nir/src/main/scala/scala/scalanative/nir/serialization/BinarySerializer.scala +++ b/nir/src/main/scala/scala/scalanative/nir/serialization/BinarySerializer.scala @@ -424,6 +424,7 @@ final class BinarySerializer(buffer: ByteBuffer) { case Val.None => putInt(T.NoneVal) case Val.True => putInt(T.TrueVal) case Val.False => putInt(T.FalseVal) + case Val.Null => putInt(T.ZeroVal); putType(Type.Ptr) case Val.Zero(ty) => putInt(T.ZeroVal); putType(ty) case Val.Undef(ty) => putInt(T.UndefVal); putType(ty) case Val.Byte(v) => putInt(T.ByteVal); put(v) diff --git a/nscplugin/src/main/scala/scala/scalanative/nscplugin/NirCodeGen.scala b/nscplugin/src/main/scala/scala/scalanative/nscplugin/NirCodeGen.scala index 5622529705..aa32761b8c 100644 --- a/nscplugin/src/main/scala/scala/scalanative/nscplugin/NirCodeGen.scala +++ b/nscplugin/src/main/scala/scala/scalanative/nscplugin/NirCodeGen.scala @@ -250,9 +250,8 @@ abstract class NirCodeGen for (f <- sym.info.decls if f.isField) { val ty = genType(f.tpe, box = false) val name = genFieldName(f) - val rhs = if (sym.isExternModule) Val.None else Val.Zero(ty) - curClassDefns += Defn.Var(attrs, name, ty, rhs) + curClassDefns += Defn.Var(attrs, name, ty, Val.None) } } @@ -643,7 +642,7 @@ abstract class NirCodeGen case UnitTag => Val.Unit case NullTag => - Val.Zero(Rt.Object) + Val.Null case BooleanTag => if (value.booleanValue) Val.True else Val.False case ByteTag => @@ -1323,11 +1322,11 @@ abstract class NirCodeGen val comp = if (negated) Comp.Ine else Comp.Ieq right withOp Op.Comp(comp, Rt.Object, left.value, right.value) } else { - val isnull = left withOp Op.Comp(Comp.Ieq, Rt.Object, left.value, Val.Zero(Rt.Object)) + val isnull = left withOp Op.Comp(Comp.Ieq, Rt.Object, left.value, Val.Null) val cond = ValTree(isnull.value) val thenp = ContTree { focus => val right = genExpr(rightp, focus) - right withOp Op.Comp(Comp.Ieq, Rt.Object, right.value, Val.Zero(Rt.Object)) + right withOp Op.Comp(Comp.Ieq, Rt.Object, right.value, Val.Null) } val elsep = ContTree { focus => genMethodCall(NObjectEqualsMethod, statically = false, diff --git a/tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala b/tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala index 235fea4e5c..35c66f7c4f 100644 --- a/tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala +++ b/tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala @@ -353,6 +353,7 @@ object CodeGen { def genJustVal(v: Val): Unit = v match { case Val.True => str("true") case Val.False => str("false") + case Val.Null => str("null") case Val.Zero(ty) => str("zeroinitializer") case Val.Undef(ty) => str("undef") case Val.Byte(v) => str(v) diff --git a/tools/src/main/scala/scala/scalanative/nir/parser/Val.scala b/tools/src/main/scala/scala/scalanative/nir/parser/Val.scala index 3894e49d68..ade11de334 100644 --- a/tools/src/main/scala/scala/scalanative/nir/parser/Val.scala +++ b/tools/src/main/scala/scala/scalanative/nir/parser/Val.scala @@ -12,6 +12,7 @@ object Val extends Base[nir.Val] { val None = P("none".! map (_ => nir.Val.None)) val True = P("true".! map (_ => nir.Val.True)) val False = P("false".! map (_ => nir.Val.False)) + val Null = P("null".! map (_ => nir.Val.Null)) val Zero = P("zero[" ~ Type.parser ~ "]" map (nir.Val.Zero(_))) val Undef = P("undef[" ~ Type.parser ~ "]" map (nir.Val.Undef(_))) val Byte = P("byte" ~ Base.Byte map (nir.Val.Byte(_))) @@ -48,6 +49,6 @@ object Val extends Base[nir.Val] { val String = P(stringLit map (nir.Val.String(_))) override val parser: P[nir.Val] = - None | True | False | Zero | Undef | Long | Int | Short | Byte | Double | Float | NoneStruct | Struct | Array | Chars | Local | Global | Unit | Const | String + None | True | False | Null | Zero | Undef | Long | Int | Short | Byte | Double | Float | NoneStruct | Struct | Array | Chars | Local | Global | Unit | Const | String } diff --git a/tools/src/main/scala/scala/scalanative/optimizer/pass/IsLowering.scala b/tools/src/main/scala/scala/scalanative/optimizer/pass/IsLowering.scala index 4ad221335d..c3e8e5d499 100644 --- a/tools/src/main/scala/scala/scalanative/optimizer/pass/IsLowering.scala +++ b/tools/src/main/scala/scala/scalanative/optimizer/pass/IsLowering.scala @@ -15,7 +15,7 @@ class IsLowering(implicit fresh: Fresh, top: Top) extends Pass { import buf._ insts.foreach { - case Let(n, Op.Is(_, Val.Zero(_))) => + case Let(n, Op.Is(_, Val.Null | Val.Zero(_))) => let(n, Op.Copy(Val.False)) case Let(n, Op.Is(ty, obj)) => @@ -24,7 +24,7 @@ class IsLowering(implicit fresh: Fresh, top: Top) extends Pass { val thenL, elseL, contL = fresh() // check if obj is null - val isnull = let(Op.Comp(Comp.Ieq, Type.Ptr, obj, Val.Zero(Type.Ptr))) + val isnull = let(Op.Comp(Comp.Ieq, Type.Ptr, obj, Val.Null)) branch(isnull, Next(thenL), Next(elseL)) // in case it's null, result is always false label(thenL) diff --git a/tools/src/main/scala/scala/scalanative/optimizer/pass/ModuleLowering.scala b/tools/src/main/scala/scala/scalanative/optimizer/pass/ModuleLowering.scala index dd4522e2f1..721d1df3a3 100644 --- a/tools/src/main/scala/scala/scalanative/optimizer/pass/ModuleLowering.scala +++ b/tools/src/main/scala/scala/scalanative/optimizer/pass/ModuleLowering.scala @@ -47,10 +47,9 @@ class ModuleLowering(implicit top: Top, fresh: Fresh) extends Pass { case Defn.Module(attrs, clsName @ ClassRef(cls), parent, ifaces) => val clsDefn = Defn.Class(attrs, clsName, parent, ifaces) val clsTy = Type.Class(clsName) - val clsNull = Val.Zero(clsTy) val valueName = clsName member "value" - val valueDefn = Defn.Var(Attrs.None, valueName, clsTy, clsNull) + val valueDefn = Defn.Var(Attrs.None, valueName, clsTy, Val.Null) val value = Val.Global(valueName, Type.Ptr) val entry = fresh() @@ -79,7 +78,7 @@ class ModuleLowering(implicit top: Top, fresh: Fresh) extends Pass { Seq( Inst.Label(entry, Seq()), Inst.Let(self.name, Op.Load(clsTy, value)), - Inst.Let(cond.name, Op.Comp(Comp.Ine, Rt.Object, self, clsNull)), + Inst.Let(cond.name, Op.Comp(Comp.Ine, Rt.Object, self, Val.Null)), Inst.If(cond, Next(existing), Next(initialize)), Inst.Label(existing, Seq()), Inst.Ret(self), diff --git a/tools/src/test/scala/scala/scalanative/nir/ValParserTest.scala b/tools/src/test/scala/scala/scalanative/nir/ValParserTest.scala index 04d9f32b85..bc5f9fa1d3 100644 --- a/tools/src/test/scala/scala/scalanative/nir/ValParserTest.scala +++ b/tools/src/test/scala/scala/scalanative/nir/ValParserTest.scala @@ -27,6 +27,11 @@ class ValParserTest extends FlatSpec with Matchers { result should be(`false`) } + it should "parse `Val.Null`" in { + val Parsed.Success(result, _) = parser.Val.Null.parse(Val.Null.show) + result should be(Val.Null) + } + it should "parse `Val.Zero`" in { val zero: Val = Val.Zero(noTpe) val Parsed.Success(result, _) = parser.Val.Zero.parse(zero.show)