summary refs log tree commit diff
path: root/parse.c
diff options
context:
space:
mode:
authorQuentin Carbonneaux <quentin.carbonneaux@yale.edu>2017-02-15 20:17:13 -0500
committerQuentin Carbonneaux <quentin.carbonneaux@yale.edu>2017-02-15 20:17:13 -0500
commita9d81338b19f21f7220e340a1c50870b40587120 (patch)
tree509f8ec05d8b8da88b90630031cff5b52b2e5c54 /parse.c
parent249af91ff9d9ffbd8962efcad999de442e609658 (diff)
downloadroux-a9d81338b19f21f7220e340a1c50870b40587120.tar.gz
add support for closure calls
Compiling languages with closures often requires passing
an extra environment parameter to the called function.

One solution is to use a convention, and reserve, say,
the first argument for that purpose.   However, that
makes binding to C a little less smooth.

Alternatively, QBE now provides a way to remain fully
ABI compatible with C by having a "hidden" environment
argument (marked with the keyword 'env').  Calling a
function expecting an environment from C will make the
contents of the environment undefined, but the normal
arguments will be passed without alteration.  Conversely,
calling a C function like it is a closure by passing
it an environemnt will work smoothly.
Diffstat (limited to 'parse.c')
-rw-r--r--parse.c24
1 files changed, 21 insertions, 3 deletions
diff --git a/parse.c b/parse.c
index a61ff1b..b911993 100644
--- a/parse.c
+++ b/parse.c
@@ -60,9 +60,11 @@ OpDesc opdesc[NOp] = {
 	[Oxcmp]   = { "xcmp",     1, {A(w,l,s,d), A(w,l,s,d)}, 1, 0, 0 },
 	[Oxtest]  = { "xtest",    1, {A(w,l,e,e), A(w,l,e,e)}, 1, 0, 0 },
 	[Oaddr]   = { "addr",     0, {A(m,m,e,e), A(x,x,e,e)}, 0, 1, 0 },
-	[Opar]    = { "parn",     0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0, 0 },
+	[Opar]    = { "par",      0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0, 0 },
+	[Opare]   = { "pare",     0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0, 0 },
 	[Oparc]   = { "parc",     0, {A(e,x,e,e), A(e,x,e,e)}, 0, 0, 0 },
 	[Oarg]    = { "arg",      0, {A(w,l,s,d), A(x,x,x,x)}, 0, 0, 0 },
+	[Oarge]   = { "arge",     0, {A(w,l,s,d), A(x,x,x,x)}, 0, 0, 0 },
 	[Oargc]   = { "argc",     0, {A(e,x,e,e), A(e,l,e,e)}, 0, 0, 0 },
 	[Ocall]   = { "call",     0, {A(m,m,m,m), A(x,x,x,x)}, 0, 0, 0 },
 	[Ovacall] = { "vacall",   0, {A(m,m,m,m), A(x,x,x,x)}, 0, 0, 0 },
@@ -108,6 +110,7 @@ enum {
 	Talloc2,
 
 	Tcall,
+	Tenv,
 	Tphi,
 	Tjmp,
 	Tjnz,
@@ -156,6 +159,7 @@ static char *kwmap[Ntok] = {
 	[Talloc1] = "alloc1",
 	[Talloc2] = "alloc2",
 	[Tcall] = "call",
+	[Tenv] = "env",
 	[Tphi] = "phi",
 	[Tjmp] = "jmp",
 	[Tjnz] = "jnz",
@@ -493,17 +497,25 @@ parsecls(int *tyn)
 static int
 parserefl(int arg)
 {
-	int k, ty;
+	int k, ty, env, hasenv;
 	Ref r;
 
+	hasenv = 0;
 	expect(Tlparen);
 	while (peek() != Trparen && peek() != Tdots) {
 		if (curi - insb >= NIns)
 			err("too many instructions (1)");
+		env = peek() == Tenv;
+		if (env)
+			next();
 		k = parsecls(&ty);
 		r = parseref();
 		if (req(r, R))
-			err("invalid reference argument");
+			err("invalid argument");
+		if (hasenv && env)
+			err("only one environment allowed");
+		if (env && k != Kl)
+			err("environment must be of type l");
 		if (!arg && rtype(r) != RTmp)
 			err("invalid function parameter");
 		if (k == 4)
@@ -511,12 +523,18 @@ parserefl(int arg)
 				*curi = (Ins){Oargc, R, {TYPE(ty), r}, Kl};
 			else
 				*curi = (Ins){Oparc, r, {TYPE(ty)}, Kl};
+		else if (env)
+			if (arg)
+				*curi = (Ins){Oarge, R, {r}, k};
+			else
+				*curi = (Ins){Opare, r, {R}, k};
 		else
 			if (arg)
 				*curi = (Ins){Oarg, R, {r}, k};
 			else
 				*curi = (Ins){Opar, r, {R}, k};
 		curi++;
+		hasenv |= env;
 		if (peek() == Trparen)
 			break;
 		expect(Tcomma);