Merge pull request #189 from unisonweb/topic/move-output-type

move outputType from `Lambda` to primitive `Computation`s
This commit is contained in:
Paul Chiusano 2018-06-12 09:47:53 -05:00 committed by GitHub
commit a6aa61fd4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 135 additions and 123 deletions

View File

@ -134,7 +134,7 @@ object BuiltinTypes {
}
val lam: Computation =
if (arity >= 1)
new Value.Lambda.ClosureForming(paramNames.toList, body, None, decompile)
new Value.Lambda.ClosureForming(paramNames.toList, body, decompile)
.toComputation
else try {
Return(req(Array()))

View File

@ -1,7 +1,6 @@
package org.unisonweb
import java.util.function.{LongBinaryOperator, LongPredicate, LongUnaryOperator,
DoubleBinaryOperator}
import java.util.function.{DoubleBinaryOperator, LongBinaryOperator, LongPredicate, LongUnaryOperator}
import org.unisonweb.Term.{Name, Term}
import org.unisonweb.Value.Lambda
@ -72,7 +71,7 @@ object Builtins {
}
val decompiled = Term.Id(name)
val lambda =
new Value.Lambda.ClosureForming(List(arg1, arg2, arg3), body, None, decompiled)
new Value.Lambda.ClosureForming(List(arg1, arg2, arg3), body, decompiled)
name -> Return(lambda)
}
@ -384,7 +383,7 @@ object Builtins {
(implicit A: Decode[A], B: Encode[B]): (Name, Computation) = {
val body: Computation.C1P = (r,x0,x0b) => B.encode(r, f(A.decode(x0, x0b)))
val decompile = Term.Id(name)
name -> Return(new Lambda1(arg, body, None, decompile))
name -> Return(new Lambda1(arg, body, decompile))
}
// Monomorphic one-argument function on unboxed values
@ -392,14 +391,11 @@ object Builtins {
arg: Name,
outputType: UnboxedType,
f: LongUnaryOperator): (Name, Computation) = {
val body: Computation.C1U = (r,x0) => {
// Unintuitively, we store the type of the unboxed value in `boxed`
// since that's a field we don't use for unboxed values.
r.boxed = outputType
f.applyAsLong(x0)
val body: Computation.C1U = new Computation.C1U(outputType) {
def raw(x0: U): U = f.applyAsLong(x0)
}
val decompile = Term.Id(name)
val computation = Return(new Lambda1(arg, body, Some(outputType), decompile))
val computation = Return(new Lambda1(arg, body, decompile))
name -> computation
}
@ -431,7 +427,7 @@ object Builtins {
val body: Computation.C1P = (r,x0,x0b) => {
B.encodeOp(r, f(A.decode(x0, x0b)), name, Value.fromParam(x0, x0b))
}
val lambda = new Lambda.Lambda1(arg, body, None, Term.Id(name))
val lambda = new Lambda.Lambda1(arg, body, Term.Id(name))
name -> Return(lambda)
}
@ -443,7 +439,7 @@ object Builtins {
(r,x1,x0,x1b,x0b) =>
C.encode(r, f(A.decode(x1, x1b), B.decode(x0, x0b)))
val decompiled = Term.Id(name)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, None, decompiled)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, decompiled)
name -> Return(lambda)
}
@ -460,7 +456,7 @@ object Builtins {
Value.fromParam(x0, x0b))
}
val decompiled = Term.Id(name)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, None, decompiled)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, decompiled)
name -> Return(lambda)
}
@ -475,7 +471,7 @@ object Builtins {
val body: Computation.C2P = (r,x1,x0,_,x0b) =>
B.encode(r, f(x1, A.decode(x0, x0b)))
val decompiled = Term.Id(name)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, None, decompiled)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, decompiled)
name -> Return(lambda)
}
@ -487,7 +483,7 @@ object Builtins {
name,
Value.fromParam(x1,x1b),
Value.fromParam(x0,x0b))
name -> Return(new Value.Lambda.ClosureForming(List(arg1, arg2), body, None, Term.Id(name)))
name -> Return(new Value.Lambda.ClosureForming(List(arg1, arg2), body, Term.Id(name)))
}
@ -503,7 +499,7 @@ object Builtins {
Value.fromParam(x0, x0b))
val decompiled = Term.Id(name)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, None, decompiled)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, decompiled)
name -> Return(lambda)
}
@ -522,7 +518,7 @@ object Builtins {
f(A.decode(x1, x1b), B.decode(x0, x0b))
}
val decompiled = Term.Id(name)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, None, decompiled)
val lambda = new Lambda.ClosureForming(List(arg1, arg2), body, decompiled)
name -> Return(lambda)
}
@ -553,65 +549,53 @@ object Builtins {
arg2: Name,
outputType: UnboxedType,
f: LongBinaryOperator): (Name, Computation) = {
val body: Computation.C2U = (r,x1,x0) => {
r.boxed = outputType
f.applyAsLong(x1, x0)
val body = new Computation.C2U(outputType) {
def raw(x1: U, x0: U): U = f.applyAsLong(x1, x0)
}
val decompiled = Term.Id(name)
val lam = new Lambda(List(arg1, arg2), body, Some(outputType), decompiled) {
val lam = new Lambda(List(arg1, arg2), body, decompiled) {
self =>
override def saturatedNonTailCall(args: List[Computation]) = args match {
case List(Return(Value.Unboxed(n1, _)),
Return(Value.Unboxed(n2, _))) =>
val n3 = f.applyAsLong(n1,n2) // constant fold
val c : Computation.C0 = r => { r.boxed = outputType; n3 }
c
new Computation.C0U(outputType) {
def raw: U = n3
}
case List(CompiledVar0,Return(Value.Unboxed(n, _))) =>
val c : Computation.C1U = (r,x0) => {
r.boxed = outputType
f.applyAsLong(x0, n)
new Computation.C1U(outputType) {
def raw(x0: U): U = f.applyAsLong(x0, n)
}
c
case List(CompiledVar1,Return(Value.Unboxed(n, _))) =>
val c : Computation.C2U = (r,x1,_) => {
r.boxed = outputType
f.applyAsLong(x1, n)
new Computation.C2U(outputType) {
def raw(x1: U, x0: U): U = f.applyAsLong(x1, n)
}
c
case List(Return(Value.Unboxed(n, _)), CompiledVar0) =>
val c: Computation.C1U = (r,x0) => {
r.boxed = outputType
f.applyAsLong(n,x0)
new Computation.C1U(outputType) {
def raw(x0: U): U = f.applyAsLong(n,x0)
}
c
case List(Return(Value.Unboxed(n, _)), CompiledVar1) =>
val c: Computation.C2U = (r,x1,_) => {
r.boxed = outputType
f.applyAsLong(n,x1)
new Computation.C2U(outputType) {
def raw(x1: U, x0: U): U = f.applyAsLong(n,x1)
}
c
case List(CompiledVar1,CompiledVar0) =>
val c: Computation.C2U = (r,x1,x0) => {
r.boxed = outputType
f.applyAsLong(x1,x0)
new Computation.C2U(outputType) {
def raw(x1: U, x0: U): U = f.applyAsLong(x1,x0)
}
c
case List(CompiledVar0,CompiledVar1) =>
val c: Computation.C2U = (r,x1,x0) => {
r.boxed = outputType
f.applyAsLong(x0,x1)
new Computation.C2U(outputType) {
def raw(x1: U, x0: U): U = f.applyAsLong(x0,x1)
}
c
case List(arg1: Computation.C2U, arg2: Computation.C2U) =>
val c: Computation.C2U = (r,x1,x0) => {
val x1v = arg1(r, x1, x0)
val x0v = arg2(r, x1, x0)
r.boxed = outputType
f.applyAsLong(x1v, x0v)
new Computation.C2U(outputType) {
def raw(x1: U, x0: U): U = {
val x1v = arg1.raw(x1, x0)
val x0v = arg2.raw(x1, x0)
f.applyAsLong(x1v, x0v)
}
}
c
case List(arg1,arg2) => (r,rec,top,stackU,x1,x0,stackB,x1b,x0b) => {
val x1v = eval(arg1,r,rec,top,stackU,x1,x0,stackB,x1b,x0b)
val x0v = eval(arg2,r,rec,top,stackU,x1,x0,stackB,x1b,x0b)
@ -626,16 +610,14 @@ object Builtins {
case Term.Compiled(p: Param) =>
// cast is okay, because in `fuu_u`, args are Unboxed.
val n = p.toValue.asInstanceOf[Value.Unboxed].n
val body: Computation =
(r,rec,top,stackU,x1,x0,stackB,x1b,x0b) => {
r.boxed = outputType
f.applyAsLong(n, x0)
}
new Lambda(self.names drop argCount, body, unboxedType,
val body = new Computation.C1U(outputType) {
def raw(x0: U): U = f.applyAsLong(n, x0)
}
new Lambda(self.names drop argCount, body,
Term.Apply(decompiled, term))
case _ => sys.error("")
}
case _ => sys.error("unpossible")
case _ => sys.error("can't underapply a function of 2 args with anything but 1 arg")
}
}
name -> Return(lam)

View File

@ -59,7 +59,6 @@ object Value {
class Lambda(
final val names: List[Name],
final val body: Computation,
final val unboxedType: Option[UnboxedType],
// the lambda decompiled form may have one free var, referring to itself
decompileWithPossibleFreeVar: Term) extends Value { self =>
@ -81,14 +80,25 @@ object Value {
def compose(f: Lambda): Lambda = {
assert(arity == 1)
val k: Computation = (r, rec, top, stackU, x1, x0, stackB, x1b, x0b) => {
val v = evalLam(f,r,top,stackU,x1,x0,stackB,x1b,x0b)
val vb = r.boxed
self(r,top,stackU,U0,v,stackB,null,vb)
val k: Computation = (self.body, f.body) match {
case (left: Computation.C1U, right: Computation.C1U) =>
new compilation.Computation.C1U(left.outputType) {
def raw(x0: U): U = left.raw(right.raw(x0))
}
case (left: Computation.C1U, right: Computation.C2U) =>
new Computation.C2U(left.outputType) {
def raw(x1: U, x0: U): U = left.raw(right.raw(x1, x0))
}
case _ =>
(r, rec, top, stackU, x1, x0, stackB, x1b, x0b) => {
val v = evalLam(f,r,top,stackU,x1,x0,stackB,x1b,x0b)
val vb = r.boxed
self(r,top,stackU,U0,v,stackB,null,vb)
}
}
val compose = Term.Lam('f, 'g, 'x)('f.v('g.v('x))) // todo: intern this
new Lambda(f.names, k, self.unboxedType,
compose(self.decompile, f.decompile))
new Lambda(f.names, k, compose(self.decompile, f.decompile))
}
def saturatedNonTailCall(args: List[Computation]): Computation =
@ -112,37 +122,33 @@ object Value {
object Lambda {
final def toValue = this
def apply(arity: Int, body: Computation, unboxedType: Option[UnboxedType],
decompile: Term) = {
def apply(arity: Int, body: Computation, decompile: Term) = {
new Lambda(names = decompile match { case Term.Lam(names, _) => names },
body, unboxedType, decompile)
body, decompile)
}
def unapply(l: Lambda): Option[(Int, Computation, Term)] =
Some((l.arity, l.body, l.decompile))
val identity: Lambda1 = {
val c: Computation = (r,rec,top,stackU,x1,x0,stackB,x1b,x0b) => {
val c: Computation.C1P = (r,x0,x0b) => {
r.boxed = x0b.toValue
x0
}
Lambda1("x", c, None, Term.Lam('x)('x))
Lambda1("x", c, Term.Lam('x)('x))
}
/** A `Lambda` of arity 1. */
// todo: delete this and ClosureForming2 later
case class Lambda1(arg1: Name, _body: Computation,
outputType: Option[UnboxedType], decompiled: Term)
extends Lambda(names = List(arg1),_body,outputType,decompiled) {
case class Lambda1(arg1: Name, _body: Computation, decompiled: Term)
extends Lambda(names = List(arg1),_body,decompiled) {
override def underapply(builtins: Environment)(
argCount: Arity, substs: Map[Name, Term]): Lambda =
sys.error("a lambda with arity 1 cannot be underapplied")
}
class ClosureForming(names: List[Name], body: Computation,
outputType: Option[UnboxedType],
decompiled: Term)
extends Lambda(names,body,outputType,decompiled) { self =>
class ClosureForming(names: List[Name], body: Computation, decompiled: Term)
extends Lambda(names,body,decompiled) { self =>
val namesArray = names.toArray
/** Underapply this `Lambda`, passing 1 argument (named `substName`). */
@ -157,8 +163,16 @@ object Value {
val body2: Computation = arity match {
// stack passed to `body2`: [a]
// stack passed to `body` : [arg,a]
case 2 => (r,rec,top,stackU,x1,x0,stackB,x1b,x0b) =>
body(r,rec,top,stackU,argv,x0,stackB,argvb,x0b)
case 2 =>
body match {
case c2u: Computation.C2U => new Computation.C1U(c2u.outputType) {
def raw(x0: U): U = c2u.raw(argv,x0)
}
case _ =>
(r,rec,top,stackU,x1,x0,stackB,x1b,x0b) =>
body(r,rec,top,stackU,argv,x0,stackB,argvb,x0b)
}
// stack passed to `body2`: [a,b]
// stack passed to `body` : [arg,a,b]
case 3 => (r,rec,top,stackU,x1,x0,stackB,x1b,x0b) => {
@ -181,7 +195,7 @@ object Value {
body(r,rec,top.inc,stackU,x1,x0,stackB,x1b,x0b)
}
}
new ClosureForming(names drop 1, body2, outputType, decompiled(substTerm))
new ClosureForming(names drop 1, body2, decompiled(substTerm))
}
// todo: try for more efficient implementation of underapply, O(n) vs n^2

View File

@ -12,23 +12,22 @@ object UnisonToScala {
def toUnboxed1(f: Value.Lambda): Env => Unboxed.F1[Param,Value] = {
require (f.arity == 1)
f.unboxedType.map[Env => Unboxed.F1[Param,Value]] {
outputType =>
env => {
f.body match {
case body: Computation.C1U =>
_env =>
new Unboxed.F1[Param, Value] {
def apply[x] = kvx => (u1,a,u2,x) => kvx(body.raw(u1), body.outputType, u2, x)
}
case _body =>
env =>
val (stackU, stackB, top, r) = env
f.body match {
case body: Computation.C1U => new Unboxed.F1[Param,Value] {
def apply[x] = kvx => (u1,a,u2,x) => kvx(body(r,u1), outputType, u2, x)
}
case body => new Unboxed.F1[Param,Value] {
def apply[x] = kvx => (u1,a,u2,x) => {
val out = evalLam(f, r, top, stackU, U0, u1, stackB, null, a)
kvx(out, r.boxed, u2, x)
}
new Unboxed.F1[Param,Value] {
def apply[x] = kvx => (u1,a,u2,x) => {
val out = evalLam(f, r, top, stackU, U0, u1, stackB, null, a)
kvx(out, r.boxed, u2, x)
}
}
}
}.getOrElse(sys.error("`f` is expected to have an unboxed output type"))
}
}
def toUnboxed2(p: (Term.Name, Computation)): Env => Unboxed.F2[Value,Value,Value] =
@ -36,22 +35,22 @@ object UnisonToScala {
def toUnboxed2(f: Value.Lambda): Env => Unboxed.F2[Value,Value,Value] = {
require(f.arity == 2)
f.unboxedType.map[Env => Unboxed.F2[Value,Value,Value]] {
outputType =>
env => {
f.body match {
case body: Computation.C2U =>
_env =>
new Unboxed.F2[Param, Param, Value] {
def apply[x] = kvx => (u1, a, u2, b, u3, x) => kvx(body.raw(u1, u2), body.outputType, u3, x)
}
case _body =>
env =>
val (stackU, stackB, top, r) = env
f.body match {
case body: Computation.C2U => new Unboxed.F2[Param, Param, Value] {
def apply[x] = kvx => (u1, a, u2, b, u3, x) => kvx(body(r, u1, u2), outputType, u3, x)
}
case body => new Unboxed.F2[Param, Param, Value] {
def apply[x] = kvx => (u1, a, u2, b, u3, x) => {
val out = evalLam(f, r, top, stackU, u1, u2, stackB, a, b)
kvx(out, r.boxed, u3, x)
}
new Unboxed.F2[Param, Param, Value] {
def apply[x] = kvx => (u1, a, u2, b, u3, x) => {
val out = evalLam(f, r, top, stackU, u1, u2, stackB, a, b)
kvx(out, r.boxed, u3, x)
}
}
}
}.getOrElse(sys.error("`f` is expected to have an unboxed output type"))
}
}
}

View File

@ -129,18 +129,31 @@ package object compilation {
// Special cases for computations that take unboxed arguments and produce unboxed results,
// and which are guaranteed not to throw tail call exceptions during evaluation. We check for
// these in various places to emit more efficient code for common cases.
abstract class C2U extends Computation {
def apply(r: R, x1: U, x0: U): U
abstract class C2U(val outputType: UnboxedType) extends Computation {
def raw(x1: U, x0: U): U
final def apply(r: R, x1: U, x0: U): U = {
r.boxed = outputType
raw(x1, x0)
}
final def apply(r: R, rec: Lambda, top: StackPtr, stackU: Array[U], x1: U, x0: U, stackB: Array[B], x1b: B, x0b: B): U =
apply(r, x1, x0)
}
abstract class C1U extends C2U {
def apply(r: R, x0: U): U
final def apply(r: R, x1: U, x0: U): U = apply(r, x0)
abstract class C1U(outputType: UnboxedType) extends C2U(outputType) {
def raw(x0: U): U
final def raw(x1: U, x0: U) = raw(x0)
final def apply(r: R, x0: U): U = {
r.boxed = outputType
raw(x0)
}
}
abstract class C0U extends C1U {
def apply(r: R): U
final def apply(r: R, x0: U): U = apply(r)
abstract class C0U(outputType: UnboxedType) extends C1U(outputType) {
def raw: U
final def raw(x0: U) = raw
final def apply(r: R): U = {
r.boxed = outputType
raw
}
}
abstract class C2P extends Computation {
def apply(r: R, x1: U, x0: U, x1b: B, x0b: B): U
@ -783,6 +796,9 @@ package object compilation {
val needsCopy = names.length > K
// inner lambda may throw SelfCall, so we create wrapper Lambda
// to process those
// note: no reason to specialize for Computation.C{2,1,0}U, as these
// won't contain a tail-recursive call, thus won't enter here
val outerLambdaBody: Computation = (r,rec,top,stackU,x1,x0,stackB,x1b,x0b) => {
val stackArgsCount = (innerLambda.arity - K) max 0
@ -818,7 +834,7 @@ package object compilation {
go(x1, x0, x1b, x0b)
}
Lambda(names.length, outerLambdaBody, innerLambda.unboxedType, innerLambda.decompile)
Lambda(names.length, outerLambdaBody, innerLambda.decompile)
}
compiledLambda match {
@ -877,7 +893,7 @@ package object compilation {
val shadowedRec = bodyRec.shadow(names)
val cbody = compile(builtins)(body, names.reverse.toVector,
shadowedRec, shadowedRec.toRecursiveVars, IsTail)
Return(Lambda(names.length, cbody, None, e))
Return(Lambda(names.length, cbody, e))
}
// 2.
else {
@ -1294,9 +1310,10 @@ package object compilation {
// Note: We have to make up a name here. "handler" works.
Term.Lam(k.names:_*)(Term.Handle(Term.Compiled(handler))(
k.decompile(k.names.map(Term.Var(_)):_*)))
// todo: worth it to specialize doIt for k.body: Computation.C{2,1,0}U?
val body: Computation = (r,rec,top,stackU,x1,x0,stackB,x1b,x0b) =>
doIt(handler, k.body)(r,rec,top,stackU,x1,x0,stackB,x1b,x0b)
Lambda(k.arity, body, k.unboxedType, decompiled)
Lambda(k.arity, body, decompiled)
}
def apply(r: R, rec: Lambda, top: StackPtr,
stackU: Array[U], x1: U, x0: U,

View File

@ -157,7 +157,7 @@ object CompilationTests {
}
val lam = Term.Compiled(
new ClosureForming(List("a","b","c","d"), body, Some(UnboxedType.Int64), 42))
new ClosureForming(List("a","b","c","d"), body, 42))
val p = Let('f -> lam(1))('f.v(2,3,4))
val p2 = Let('f -> lam(1), 'g -> 'f.v(2))('g.v(3,4))
val p3 = Let('f -> lam(1), 'g -> 'f.v(2), 'h -> 'g.v(3))('h.v(4))