Skip to content

Commit

Permalink
Preliminary support for segfault-based safepoints (scala-native#711)
Browse files Browse the repository at this point in the history
* Add support for volatile loads and stores to NIR

* Add support for alignment attribute

* Preliminary support for segfault-based safepoints
  • Loading branch information
densh authored and muxanick committed May 25, 2017
1 parent 2ce212b commit 0f5e3e9
Show file tree
Hide file tree
Showing 18 changed files with 244 additions and 70 deletions.
24 changes: 11 additions & 13 deletions nativelib/src/main/resources/gc/boehm/gc.c
@@ -1,26 +1,24 @@
#include <gc.h>
#include <stdlib.h>
#include <stdio.h>

// 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() {}
26 changes: 15 additions & 11 deletions nativelib/src/main/resources/gc/none/gc.c
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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() {}
34 changes: 34 additions & 0 deletions nativelib/src/main/resources/safepoint.c
@@ -0,0 +1,34 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

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);
}
10 changes: 8 additions & 2 deletions nir/src/main/scala/scala/scalanative/nir/Attrs.scala
Expand Up @@ -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
Expand All @@ -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]

Expand All @@ -46,6 +49,7 @@ final case class Attrs(inline: Inline = MayInline,
overrides.foreach { out += Override(_) }
out ++= pins
out ++= links
align.foreach { out += Align(_) }

out
}
Expand All @@ -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]
Expand All @@ -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)
}
}
14 changes: 10 additions & 4 deletions nir/src/main/scala/scala/scalanative/nir/Ops.scala
Expand Up @@ -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
Expand Down Expand Up @@ -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])
Expand All @@ -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
Expand Down
12 changes: 8 additions & 4 deletions nir/src/main/scala/scala/scalanative/nir/Show.scala
Expand Up @@ -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 =>
Expand Down Expand Up @@ -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)
Expand Down
Expand Up @@ -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)
Expand Down
Expand Up @@ -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]
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
24 changes: 17 additions & 7 deletions tools/src/main/scala/scala/scalanative/codegen/CodeGen.scala
Expand Up @@ -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) =>
Expand All @@ -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,
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -536,6 +543,9 @@ object CodeGen {
newline()
genBind()
str("store ")
if (isVolatile) {
str("volatile ")
}
genVal(value)
str(", ")
genType(ty)
Expand Down
5 changes: 4 additions & 1 deletion tools/src/main/scala/scala/scalanative/nir/parser/Attr.scala
Expand Up @@ -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 =
Expand All @@ -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

}

0 comments on commit 0f5e3e9

Please sign in to comment.