From 0f5e3e956e9c68a1fca76c1c11d713f0b9f52b4b Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Tue, 9 May 2017 11:39:00 +0200 Subject: [PATCH] Preliminary support for segfault-based safepoints (#711) * Add support for volatile loads and stores to NIR * Add support for alignment attribute * Preliminary support for segfault-based safepoints --- nativelib/src/main/resources/gc/boehm/gc.c | 24 ++++----- nativelib/src/main/resources/gc/none/gc.c | 26 ++++++---- nativelib/src/main/resources/safepoint.c | 34 +++++++++++++ .../scala/scala/scalanative/nir/Attrs.scala | 10 +++- .../scala/scala/scalanative/nir/Ops.scala | 14 ++++-- .../scala/scala/scalanative/nir/Show.scala | 12 +++-- .../serialization/BinaryDeserializer.scala | 4 +- .../nir/serialization/BinarySerializer.scala | 14 +++++- .../scala/scalanative/codegen/CodeGen.scala | 24 ++++++--- .../scala/scalanative/nir/parser/Attr.scala | 5 +- .../scala/scalanative/nir/parser/Op.scala | 10 ++-- .../scala/scalanative/optimizer/Driver.scala | 4 +- .../scala/scalanative/optimizer/Pass.scala | 8 +-- .../optimizer/inject/SafepointTrigger.scala | 27 ++++++++++ .../optimizer/pass/GlobalValueNumbering.scala | 7 +-- .../optimizer/pass/SafepointInsertion.scala | 50 +++++++++++++++++++ .../scalanative/nir/AttrParserTest.scala | 25 ++++++---- .../scala/scalanative/nir/OpParserTest.scala | 16 +++++- 18 files changed, 244 insertions(+), 70 deletions(-) create mode 100644 nativelib/src/main/resources/safepoint.c create mode 100644 tools/src/main/scala/scala/scalanative/optimizer/inject/SafepointTrigger.scala create mode 100755 tools/src/main/scala/scala/scalanative/optimizer/pass/SafepointInsertion.scala diff --git a/nativelib/src/main/resources/gc/boehm/gc.c b/nativelib/src/main/resources/gc/boehm/gc.c index d5719573a7b..81913e9a877 100644 --- a/nativelib/src/main/resources/gc/boehm/gc.c +++ b/nativelib/src/main/resources/gc/boehm/gc.c @@ -1,26 +1,24 @@ #include +#include +#include // At the moment we rely on the conservative // mode of Boehm GC as our garbage collector. -void scalanative_init() { - GC_init(); -} +void scalanative_init() { GC_init(); } -void* scalanative_alloc(void* info, size_t size) { - void** alloc = (void**) GC_malloc(size); +void *scalanative_alloc(void *info, size_t size) { + void **alloc = (void **)GC_malloc(size); *alloc = info; - return (void*) alloc; + return (void *)alloc; } -void* scalanative_alloc_raw(size_t size) { - return GC_malloc(size); -} +void *scalanative_alloc_raw(size_t size) { return GC_malloc(size); } -void* scalanative_alloc_raw_atomic(size_t size) { +void *scalanative_alloc_raw_atomic(size_t size) { return GC_malloc_atomic(size); } -void scalanative_collect() { - GC_gcollect(); -} +void scalanative_collect() { GC_gcollect(); } + +void scalanative_safepoint() {} diff --git a/nativelib/src/main/resources/gc/none/gc.c b/nativelib/src/main/resources/gc/none/gc.c index 8f6920d17fe..cf04a95a00b 100644 --- a/nativelib/src/main/resources/gc/none/gc.c +++ b/nativelib/src/main/resources/gc/none/gc.c @@ -9,7 +9,7 @@ // Dummy GC that maps chunks of 4GB and allocates but never frees. // Map 4GB -#define CHUNK (4*1024*1024*1024L) +#define CHUNK (4 * 1024 * 1024 * 1024L) // Allow read and write #define DUMMY_GC_PROT (PROT_READ | PROT_WRITE) // Map private anonymous memory, and prevent from reserving swap @@ -18,20 +18,22 @@ #define DUMMY_GC_FD -1 #define DUMMY_GC_FD_OFFSET 0 +void *current = 0; +void *end = 0; -void* current = 0; -void* end = 0; - +void scalanative_safepoint_init(); void scalanative_init() { - current = mmap(NULL, CHUNK, DUMMY_GC_PROT, DUMMY_GC_FLAGS, DUMMY_GC_FD, DUMMY_GC_FD_OFFSET); + current = mmap(NULL, CHUNK, DUMMY_GC_PROT, DUMMY_GC_FLAGS, DUMMY_GC_FD, + DUMMY_GC_FD_OFFSET); end = current + CHUNK; + scalanative_safepoint_init(); } -void* scalanative_alloc_raw(size_t size) { +void *scalanative_alloc_raw(size_t size) { size = size + (8 - size % 8); if (current != 0 && current + size < end) { - void* alloc = current; + void *alloc = current; current += size; return alloc; } else { @@ -40,14 +42,16 @@ void* scalanative_alloc_raw(size_t size) { } } -void* scalanative_alloc_raw_atomic(size_t size) { +void *scalanative_alloc_raw_atomic(size_t size) { return scalanative_alloc_raw(size); } -void* scalanative_alloc(void* info, size_t size) { - void** alloc = (void**) scalanative_alloc_raw(size); +void *scalanative_alloc(void *info, size_t size) { + void **alloc = (void **)scalanative_alloc_raw(size); *alloc = info; - return (void*) alloc; + return (void *)alloc; } void scalanative_collect() {} + +void scalanative_safepoint() {} diff --git a/nativelib/src/main/resources/safepoint.c b/nativelib/src/main/resources/safepoint.c new file mode 100644 index 00000000000..b17aab2899e --- /dev/null +++ b/nativelib/src/main/resources/safepoint.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +extern unsigned char scalanative_safepoint_trigger[4096] __attribute__((aligned(4096))); + +void scalanative_safepoint(); + +void scalanative_safepoint_on() { + mprotect((void*) &scalanative_safepoint_trigger, 4096, PROT_NONE); +} + +void scalanative_safepoint_off() { + mprotect((void*) &scalanative_safepoint_trigger, 4096, PROT_READ); +} + +void scalanative_safepoint_handler(int sig, siginfo_t *info, void *ucontext) { + if (info->si_addr == (void*) &scalanative_safepoint_trigger) { + scalanative_safepoint(); + } else { + printf("Segfault %d at %p", sig, info->si_addr); + exit(1); + } +} + +void scalanative_safepoint_init() { + struct sigaction action; + action.sa_sigaction = scalanative_safepoint_handler; + action.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&action.sa_mask); + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGBUS, &action, NULL); +} diff --git a/nir/src/main/scala/scala/scalanative/nir/Attrs.scala b/nir/src/main/scala/scala/scalanative/nir/Attrs.scala index 3d45cb054c3..e51e8a8cd1f 100644 --- a/nir/src/main/scala/scala/scalanative/nir/Attrs.scala +++ b/nir/src/main/scala/scala/scalanative/nir/Attrs.scala @@ -21,6 +21,8 @@ object Attr { final case object Extern extends Attr final case class Override(name: Global) extends Attr + final case class Align(value: Int) extends Attr + // Linker attributes final case class Link(name: String) extends Attr sealed abstract class Pin extends Attr @@ -35,7 +37,8 @@ final case class Attrs(inline: Inline = MayInline, isDyn: Boolean = false, overrides: Seq[Global] = Seq(), pins: Seq[Pin] = Seq(), - links: Seq[Attr.Link] = Seq()) { + links: Seq[Attr.Link] = Seq(), + align: Option[Int] = scala.None) { def toSeq: Seq[Attr] = { val out = mutable.UnrolledBuffer.empty[Attr] @@ -46,6 +49,7 @@ final case class Attrs(inline: Inline = MayInline, overrides.foreach { out += Override(_) } out ++= pins out ++= links + align.foreach { out += Align(_) } out } @@ -58,6 +62,7 @@ object Attrs { var isPure = false var isExtern = false var isDyn = false + var align = Option.empty[Int] val overrides = mutable.UnrolledBuffer.empty[Global] val pins = mutable.UnrolledBuffer.empty[Pin] val links = mutable.UnrolledBuffer.empty[Attr.Link] @@ -67,11 +72,12 @@ object Attrs { case Pure => isPure = true case Extern => isExtern = true case Dyn => isDyn = true + case Align(value) => align = Some(value) case Override(name) => overrides += name case attr: Pin => pins += attr case link: Attr.Link => links += link } - new Attrs(inline, isPure, isExtern, isDyn, overrides, pins, links) + new Attrs(inline, isPure, isExtern, isDyn, overrides, pins, links, align) } } diff --git a/nir/src/main/scala/scala/scalanative/nir/Ops.scala b/nir/src/main/scala/scala/scalanative/nir/Ops.scala index cf09bb7df05..b2ed098a6b4 100644 --- a/nir/src/main/scala/scala/scalanative/nir/Ops.scala +++ b/nir/src/main/scala/scala/scalanative/nir/Ops.scala @@ -7,8 +7,8 @@ sealed abstract class Op { final def resty: Type = this match { case Op.Call(Type.Function(_, ret), _, _, _) => ret case Op.Call(_, _, _, _) => unreachable - case Op.Load(ty, _) => ty - case Op.Store(_, _, _) => Type.Unit + case Op.Load(ty, _, _) => ty + case Op.Store(_, _, _, _) => Type.Unit case Op.Elem(_, _, _) => Type.Ptr case Op.Extract(aggr, indexes) => aggr.ty.elemty(indexes.map(Val.Int(_))) case Op.Insert(aggr, _, _) => aggr.ty @@ -43,8 +43,9 @@ object Op { // low-level final case class Call(ty: Type, ptr: Val, args: Seq[Val], unwind: Next) extends Unwind - final case class Load(ty: Type, ptr: Val) extends Op - final case class Store(ty: Type, ptr: Val, value: Val) extends Op + final case class Load(ty: Type, ptr: Val, isVolatile: Boolean) extends Op + final case class Store(ty: Type, ptr: Val, value: Val, isVolatile: Boolean) + extends Op final case class Elem(ty: Type, ptr: Val, indexes: Seq[Val]) extends Pure final case class Extract(aggr: Val, indexes: Seq[Int]) extends Pure final case class Insert(aggr: Val, value: Val, indexes: Seq[Int]) @@ -55,6 +56,11 @@ object Op { final case class Conv(conv: nir.Conv, ty: Type, value: Val) extends Pure final case class Select(cond: Val, thenv: Val, elsev: Val) extends Pure + def Load(ty: Type, ptr: Val): Load = + Load(ty, ptr, isVolatile = false) + def Store(ty: Type, ptr: Val, value: Val): Store = + Store(ty, ptr, value, isVolatile = false) + // high-level final case class Classalloc(name: Global) extends Op final case class Field(obj: Val, name: Global) extends Op diff --git a/nir/src/main/scala/scala/scalanative/nir/Show.scala b/nir/src/main/scala/scala/scalanative/nir/Show.scala index 6494da8bde7..bf0e346ec52 100644 --- a/nir/src/main/scala/scala/scalanative/nir/Show.scala +++ b/nir/src/main/scala/scala/scalanative/nir/Show.scala @@ -55,6 +55,10 @@ object Show { str("alwaysinline") case Attr.Dyn => str("dyn") + case Attr.Align(value) => + str("align(") + str(value) + str(")") case Attr.Pure => str("pure") case Attr.Extern => @@ -177,13 +181,13 @@ object Show { str(" ") next_(unwind) } - case Op.Load(ty, ptr) => - str("load[") + case Op.Load(ty, ptr, isVolatile) => + str(if (isVolatile) "volatile load[" else "load[") type_(ty) str("] ") val_(ptr) - case Op.Store(ty, ptr, value) => - str("store[") + case Op.Store(ty, ptr, value, isVolatile) => + str(if (isVolatile) "volatile store[" else "store[") type_(ty) str("] ") val_(ptr) 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 bbe0588bbb6..993d73d0862 100644 --- a/nir/src/main/scala/scala/scalanative/nir/serialization/BinaryDeserializer.scala +++ b/nir/src/main/scala/scala/scalanative/nir/serialization/BinaryDeserializer.scala @@ -228,8 +228,8 @@ final class BinaryDeserializer(_buffer: => ByteBuffer) { private def getOp(): Op = getInt match { case T.CallOp => Op.Call(getType, getVal, getVals, getNext) - case T.LoadOp => Op.Load(getType, getVal) - case T.StoreOp => Op.Store(getType, getVal, getVal) + case T.LoadOp => Op.Load(getType, getVal, isVolatile = false) + case T.StoreOp => Op.Store(getType, getVal, getVal, isVolatile = false) case T.ElemOp => Op.Elem(getType, getVal, getVals) case T.ExtractOp => Op.Extract(getVal, getInts) case T.InsertOp => Op.Insert(getVal, getVal, getInts) 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 1f3ff1c8c0e..802906a08da 100644 --- a/nir/src/main/scala/scala/scalanative/nir/serialization/BinarySerializer.scala +++ b/nir/src/main/scala/scala/scalanative/nir/serialization/BinarySerializer.scala @@ -9,6 +9,11 @@ import nir.serialization.{Tags => T} final class BinarySerializer(buffer: ByteBuffer) { import buffer._ + // Things to change in next binary-breaking release: + // 1. Val.Null should have its own tag, not encoded via Val.Zero(Type.Ptr). + // 2. Volatile Op.{Load, Store} should become serializable. + // 3. Attr.Align should become serializable; + final def serialize(defns: Seq[Defn]): Unit = { val names = defns.map(_.name) val positions = mutable.UnrolledBuffer.empty[Int] @@ -67,6 +72,9 @@ final class BinarySerializer(buffer: ByteBuffer) { case Attr.Dyn => putInt(T.DynAttr) + case Attr.Align(_) => + assert(false, "alignment attribute is not serializable") + case Attr.Pure => putInt(T.PureAttr) case Attr.Extern => putInt(T.ExternAttr) case Attr.Override(n) => putInt(T.OverrideAttr); putGlobal(n) @@ -264,12 +272,14 @@ final class BinarySerializer(buffer: ByteBuffer) { putVals(args) putNext(unwind) - case Op.Load(ty, ptr) => + case Op.Load(ty, ptr, isVolatile) => + assert(!isVolatile, "volatile loads are not serializable") putInt(T.LoadOp) putType(ty) putVal(ptr) - case Op.Store(ty, value, ptr) => + case Op.Store(ty, value, ptr, isVolatile) => + assert(!isVolatile, "volatile stores are not serializable") putInt(T.StoreOp) putType(ty) putVal(value) diff --git a/tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala b/tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala index 1154d8f14ab..e2261fecb67 100644 --- a/tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala +++ b/tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala @@ -153,9 +153,9 @@ object CodeGen { case Defn.Struct(attrs, name, tys) => genStruct(attrs, name, tys) case Defn.Var(attrs, name, ty, rhs) => - genGlobalDefn(name, attrs.isExtern, isConst = false, ty, rhs) + genGlobalDefn(attrs, name, isConst = false, ty, rhs) case Defn.Const(attrs, name, ty, rhs) => - genGlobalDefn(name, attrs.isExtern, isConst = true, ty, rhs) + genGlobalDefn(attrs, name, isConst = true, ty, rhs) case Defn.Declare(attrs, name, sig) => genFunctionDefn(attrs, name, sig, Seq()) case Defn.Define(attrs, name, sig, blocks) => @@ -174,21 +174,25 @@ object CodeGen { str("}") } - def genGlobalDefn(name: nir.Global, - isExtern: Boolean, + def genGlobalDefn(attrs: Attrs, + name: nir.Global, isConst: Boolean, ty: nir.Type, rhs: nir.Val): Unit = { str("@") genGlobal(name) str(" = ") - str(if (isExtern) "external " else "") + str(if (attrs.isExtern) "external " else "") str(if (isConst) "constant" else "global") str(" ") rhs match { case Val.None => genType(ty) case rhs => genVal(rhs) } + attrs.align.foreach { value => + str(", align ") + str(value) + } } def genFunctionDefn(attrs: Attrs, @@ -500,7 +504,7 @@ object CodeGen { case call: Op.Call => genCall(genBind, call) - case Op.Load(ty, ptr) => + case Op.Load(ty, ptr, isVolatile) => val pointee = fresh() newline() @@ -515,13 +519,16 @@ object CodeGen { newline() genBind() str("load ") + if (isVolatile) { + str("volatile ") + } genType(ty) str(", ") genType(ty) str("* %") genLocal(pointee) - case Op.Store(ty, ptr, value) => + case Op.Store(ty, ptr, value, isVolatile) => val pointee = fresh() newline() @@ -536,6 +543,9 @@ object CodeGen { newline() genBind() str("store ") + if (isVolatile) { + str("volatile ") + } genVal(value) str(", ") genType(ty) diff --git a/tools/src/main/scala/scala/scalanative/nir/parser/Attr.scala b/tools/src/main/scala/scala/scalanative/nir/parser/Attr.scala index 7a3ac65b1c1..7eb903466d3 100644 --- a/tools/src/main/scala/scala/scalanative/nir/parser/Attr.scala +++ b/tools/src/main/scala/scala/scalanative/nir/parser/Attr.scala @@ -13,6 +13,8 @@ object Attr extends Base[nir.Attr] { val InlineHint = P("inlinehint".! map (_ => nir.Attr.InlineHint)) val NoInline = P("noinline".! map (_ => nir.Attr.NoInline)) val AlwaysInline = P("alwaysinline".! map (_ => nir.Attr.AlwaysInline)) + val Dyn = P("dyn".! map (_ => nir.Attr.Dyn)) + val Align = P(("align(" ~ int ~ ")") map (nir.Attr.Align(_))) val Pure = P("pure".! map (_ => nir.Attr.Pure)) val Extern = P("extern".! map (_ => nir.Attr.Extern)) val Override = @@ -23,8 +25,9 @@ object Attr extends Base[nir.Attr] { P("pin-if(" ~ Global.parser ~ "," ~ Global.parser ~ ")" map { case (name, cond) => nir.Attr.PinIf(name, cond) }) + val PinWeak = P("pin-weak(" ~ Global.parser ~ ")" map (nir.Attr.PinWeak(_))) override val parser: P[nir.Attr] = - MayInline | InlineHint | NoInline | AlwaysInline | Pure | Extern | Override | Link | PinAlways | PinIf + MayInline | InlineHint | NoInline | AlwaysInline | Dyn | Align | Pure | Extern | Override | Link | PinAlways | PinIf | PinWeak } diff --git a/tools/src/main/scala/scala/scalanative/nir/parser/Op.scala b/tools/src/main/scala/scala/scalanative/nir/parser/Op.scala index fc31a3c162c..b40c7ab3fdd 100644 --- a/tools/src/main/scala/scala/scalanative/nir/parser/Op.scala +++ b/tools/src/main/scala/scala/scalanative/nir/parser/Op.scala @@ -19,12 +19,14 @@ object Op extends Base[nir.Op] { case (ty, f, args, unwind) => nir.Op.Call(ty, f, args, unwind) } val Load = - P("load[" ~ Type.parser ~ "]" ~ Val.parser map { - case (ty, ptr) => nir.Op.Load(ty, ptr) + P("volatile".!.? ~ "load[" ~ Type.parser ~ "]" ~ Val.parser map { + case (volatile, ty, ptr) => + nir.Op.Load(ty, ptr, isVolatile = volatile.nonEmpty) }) val Store = - P("store[" ~ Type.parser ~ "]" ~ Val.parser ~ "," ~ Val.parser map { - case (ty, ptr, value) => nir.Op.Store(ty, ptr, value) + P("volatile".!.? ~ "store[" ~ Type.parser ~ "]" ~ Val.parser ~ "," ~ Val.parser map { + case (volatile, ty, ptr, value) => + nir.Op.Store(ty, ptr, value, isVolatile = volatile.nonEmpty) }) val Elem = P( diff --git a/tools/src/main/scala/scala/scalanative/optimizer/Driver.scala b/tools/src/main/scala/scala/scalanative/optimizer/Driver.scala index b0242999353..6820f352d48 100644 --- a/tools/src/main/scala/scala/scalanative/optimizer/Driver.scala +++ b/tools/src/main/scala/scala/scalanative/optimizer/Driver.scala @@ -21,7 +21,8 @@ object Driver { inject.RuntimeTypeInformation, inject.ClassStruct, inject.ObjectArrayId, - inject.ModuleArray + inject.ModuleArray, + inject.SafepointTrigger ) private val fastOptPasses = Seq( @@ -61,6 +62,7 @@ object Driver { pass.SizeofLowering, pass.CopyPropagation, pass.DeadCodeElimination + // pass.SafepointInsertion ) /** Create driver with default pipeline for this configuration. */ diff --git a/tools/src/main/scala/scala/scalanative/optimizer/Pass.scala b/tools/src/main/scala/scala/scalanative/optimizer/Pass.scala index aeb6e24fed4..52fc7912448 100644 --- a/tools/src/main/scala/scala/scalanative/optimizer/Pass.scala +++ b/tools/src/main/scala/scala/scalanative/optimizer/Pass.scala @@ -68,10 +68,10 @@ trait Pass extends AnyPass { def onOp(op: Op): Op = op match { case Op.Call(ty, ptrv, argvs, unwind) => Op.Call(onType(ty), onVal(ptrv), argvs.map(onVal), onNext(unwind)) - case Op.Load(ty, ptrv) => - Op.Load(onType(ty), onVal(ptrv)) - case Op.Store(ty, ptrv, v) => - Op.Store(onType(ty), onVal(ptrv), onVal(v)) + case Op.Load(ty, ptrv, isVolatile) => + Op.Load(onType(ty), onVal(ptrv), isVolatile) + case Op.Store(ty, ptrv, v, isVolatile) => + Op.Store(onType(ty), onVal(ptrv), onVal(v), isVolatile) case Op.Elem(ty, ptrv, indexvs) => Op.Elem(onType(ty), onVal(ptrv), indexvs.map(onVal)) case Op.Extract(aggrv, indexvs) => diff --git a/tools/src/main/scala/scala/scalanative/optimizer/inject/SafepointTrigger.scala b/tools/src/main/scala/scala/scalanative/optimizer/inject/SafepointTrigger.scala new file mode 100644 index 00000000000..ae77fcdaa23 --- /dev/null +++ b/tools/src/main/scala/scala/scalanative/optimizer/inject/SafepointTrigger.scala @@ -0,0 +1,27 @@ +package scala.scalanative.optimizer.inject + +import scala.collection.mutable +import scala.scalanative.nir._ +import scala.scalanative.optimizer.analysis.ClassHierarchy.Top +import scala.scalanative.optimizer.{Inject, InjectCompanion} +import scala.scalanative.tools.Config + +class SafepointTrigger(top: Top) extends Inject { + import SafepointTrigger._ + + override def apply(buf: mutable.Buffer[Defn]) = + buf += safepointTriggerDefn +} + +object SafepointTrigger extends InjectCompanion { + val safepointTriggerName = Global.Top("scalanative_safepoint_trigger") + val safepointTriggerTy = Type.Array(Type.Byte, 4096) + val safepointTriggerInit = Val.Zero(safepointTriggerTy) + val safepointTriggerDefn = + Defn.Var(Attrs(align = Some(4096)), + safepointTriggerName, + safepointTriggerTy, + safepointTriggerInit) + + override def apply(config: Config, top: Top) = new SafepointTrigger(top) +} diff --git a/tools/src/main/scala/scala/scalanative/optimizer/pass/GlobalValueNumbering.scala b/tools/src/main/scala/scala/scalanative/optimizer/pass/GlobalValueNumbering.scala index a0248474816..16f2ddeb154 100644 --- a/tools/src/main/scala/scala/scalanative/optimizer/pass/GlobalValueNumbering.scala +++ b/tools/src/main/scala/scala/scalanative/optimizer/pass/GlobalValueNumbering.scala @@ -278,9 +278,10 @@ object GlobalValueNumbering extends PassCompanion { def hashOp(op: Op): Hash = { import Op._ val opFields: Seq[Any] = op match { - case Call(ty, ptr, args, _) => "Call" +: ty +: ptr +: args - case Load(ty, ptr) => Seq("Load", ty, ptr) - case Store(ty, ptr, value) => Seq("Store", ty, ptr, value) + case Call(ty, ptr, args, _) => "Call" +: ty +: ptr +: args + case Load(ty, ptr, isVolatile) => Seq("Load", ty, ptr, isVolatile) + case Store(ty, ptr, value, isVolatile) => + Seq("Store", ty, ptr, value, isVolatile) case Elem(ty, ptr, indexes) => "Elem" +: ty +: ptr +: indexes case Extract(aggr, indexes) => "Extract" +: aggr +: indexes case Insert(aggr, value, indexes) => diff --git a/tools/src/main/scala/scala/scalanative/optimizer/pass/SafepointInsertion.scala b/tools/src/main/scala/scala/scalanative/optimizer/pass/SafepointInsertion.scala new file mode 100755 index 00000000000..1b17bf0458d --- /dev/null +++ b/tools/src/main/scala/scala/scalanative/optimizer/pass/SafepointInsertion.scala @@ -0,0 +1,50 @@ +package scala.scalanative +package optimizer +package pass + +import scala.collection.mutable +import analysis.ClassHierarchy.Top +import nir._ + +/** Inserts safepoint probes before every return. */ +class SafepointInsertion(implicit fresh: Fresh) extends Pass { + import SafepointInsertion._ + + override def onDefns(defns: Seq[Defn]): Seq[Defn] = { + val buf = mutable.UnrolledBuffer.empty[Defn] + + defns.foreach { + case defn: Defn.Define if defn.attrs.inline ne Attr.AlwaysInline => + buf += defn.copy(insts = onInsts(defn.insts)) + + case defn => + buf += defn + } + + buf + } + + override def onInsts(insts: Seq[Inst]): Seq[Inst] = { + val buf = new nir.Buffer + import buf._ + + insts.foreach { + case inst: Inst.Ret => + let(Op.Load(Type.Byte, safepointTriggerVal, isVolatile = true)) + buf += inst + + case inst => + buf += inst + } + + buf.toSeq + } +} + +object SafepointInsertion extends PassCompanion { + val safepointTriggerName = Global.Top("scalanative_safepoint_trigger") + val safepointTriggerVal = Val.Global(safepointTriggerName, Type.Ptr) + + override def apply(config: tools.Config, top: Top) = + new SafepointInsertion()(top.fresh) +} diff --git a/tools/src/test/scala/scala/scalanative/nir/AttrParserTest.scala b/tools/src/test/scala/scala/scalanative/nir/AttrParserTest.scala index d36324624e8..59ee1851121 100644 --- a/tools/src/test/scala/scala/scalanative/nir/AttrParserTest.scala +++ b/tools/src/test/scala/scala/scalanative/nir/AttrParserTest.scala @@ -10,16 +10,21 @@ class AttrParserTest extends FlatSpec with Matchers { "The NIR parser" should "parse attributes" in { import Attr._ - val attrs: Seq[Attr] = Seq(MayInline, - InlineHint, - NoInline, - AlwaysInline, - Pure, - Extern, - Override(global), - Link("test"), - PinAlways(global), - PinIf(global, global)) + val attrs: Seq[Attr] = Seq( + MayInline, + InlineHint, + NoInline, + AlwaysInline, + Dyn, + Align(1024), + Pure, + Extern, + Override(global), + Link("test"), + PinAlways(global), + PinIf(global, global), + PinWeak(global) + ) attrs foreach { attr => val Parsed.Success(result, _) = parser.Attr.parser.parse(attr.show) diff --git a/tools/src/test/scala/scala/scalanative/nir/OpParserTest.scala b/tools/src/test/scala/scala/scalanative/nir/OpParserTest.scala index f10572d29b6..f156f337586 100644 --- a/tools/src/test/scala/scala/scalanative/nir/OpParserTest.scala +++ b/tools/src/test/scala/scala/scalanative/nir/OpParserTest.scala @@ -22,18 +22,30 @@ class OpParserTest extends FlatSpec with Matchers { result should be(call) } - it should "parse `Op.Load`" in { + it should "parse non-volatile `Op.Load`" in { val load: Op = Op.Load(noTpe, Val.None) val Parsed.Success(result, _) = parser.Op.Load.parse(load.show) result should be(load) } - it should "parse `Op.Store`" in { + it should "parse volatile `Op.Load`" in { + val load: Op = Op.Load(noTpe, Val.None, isVolatile = true) + val Parsed.Success(result, _) = parser.Op.Load.parse(load.show) + result should be(load) + } + + it should "parse non-volatile `Op.Store`" in { val store: Op = Op.Store(noTpe, Val.None, Val.None) val Parsed.Success(result, _) = parser.Op.Store.parse(store.show) result should be(store) } + it should "parse volatile `Op.Store`" in { + val store: Op = Op.Store(noTpe, Val.None, Val.None, isVolatile = true) + val Parsed.Success(result, _) = parser.Op.Store.parse(store.show) + result should be(store) + } + it should "parse `Op.Elem`" in { val elem: Op = Op.Elem(noTpe, Val.None, Seq.empty) val Parsed.Success(result, _) = parser.Op.Elem.parse(elem.show)