Make function calls tail-recursive
This commit is contained in:
		
							parent
							
								
									273322c773
								
							
						
					
					
						commit
						c897bac549
					
				
					 4 changed files with 65 additions and 40 deletions
				
			
		|  | @ -259,6 +259,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, con | ||||||
|     throw TypeError(format(s) % s1 % s2); |     throw TypeError(format(s) % s1 % s2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | LocalNoInlineNoReturn(void throwTypeError(const char * s, const ExprLambda & fun, const Symbol & s2)) | ||||||
|  | { | ||||||
|  |     throw TypeError(format(s) % fun.showNamePos() % s2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) | LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) | ||||||
| { | { | ||||||
|     throw AssertionError(format(s) % pos); |     throw AssertionError(format(s) % pos); | ||||||
|  | @ -700,47 +705,52 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v) | ||||||
| 
 | 
 | ||||||
| void ExprApp::eval(EvalState & state, Env & env, Value & v) | void ExprApp::eval(EvalState & state, Env & env, Value & v) | ||||||
| { | { | ||||||
|     Value vFun; |     e1->eval(state, env, v); | ||||||
|     e1->eval(state, env, vFun); |     state.callFunction(v, *(e2->maybeThunk(state, env)), v); | ||||||
|     state.callFunction(vFun, *(e2->maybeThunk(state, env)), v); | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void EvalState::callPrimOp(Value & fun, Value & arg, Value & v) | ||||||
|  | { | ||||||
|  |     /* Figure out the number of arguments still needed. */ | ||||||
|  |     unsigned int argsDone = 0; | ||||||
|  |     Value * primOp = &fun; | ||||||
|  |     while (primOp->type == tPrimOpApp) { | ||||||
|  |         argsDone++; | ||||||
|  |         primOp = primOp->primOpApp.left; | ||||||
|  |     } | ||||||
|  |     assert(primOp->type == tPrimOp); | ||||||
|  |     unsigned int arity = primOp->primOp->arity; | ||||||
|  |     unsigned int argsLeft = arity - argsDone; | ||||||
|  | 
 | ||||||
|  |     if (argsLeft == 1) { | ||||||
|  |         /* We have all the arguments, so call the primop. */ | ||||||
|  | 
 | ||||||
|  |         /* Put all the arguments in an array. */ | ||||||
|  |         Value * vArgs[arity]; | ||||||
|  |         unsigned int n = arity - 1; | ||||||
|  |         vArgs[n--] = &arg; | ||||||
|  |         for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left) | ||||||
|  |             vArgs[n--] = arg->primOpApp.right; | ||||||
|  | 
 | ||||||
|  |         /* And call the primop. */ | ||||||
|  |         nrPrimOpCalls++; | ||||||
|  |         if (countCalls) primOpCalls[primOp->primOp->name]++; | ||||||
|  |         primOp->primOp->fun(*this, vArgs, v); | ||||||
|  |     } else { | ||||||
|  |         Value * fun2 = allocValue(); | ||||||
|  |         *fun2 = fun; | ||||||
|  |         v.type = tPrimOpApp; | ||||||
|  |         v.primOpApp.left = fun2; | ||||||
|  |         v.primOpApp.right = &arg; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void EvalState::callFunction(Value & fun, Value & arg, Value & v) | void EvalState::callFunction(Value & fun, Value & arg, Value & v) | ||||||
| { | { | ||||||
|     if (fun.type == tPrimOp || fun.type == tPrimOpApp) { |     if (fun.type == tPrimOp || fun.type == tPrimOpApp) { | ||||||
| 
 |         callPrimOp(fun, arg, v); | ||||||
|         /* Figure out the number of arguments still needed. */ |  | ||||||
|         unsigned int argsDone = 0; |  | ||||||
|         Value * primOp = &fun; |  | ||||||
|         while (primOp->type == tPrimOpApp) { |  | ||||||
|             argsDone++; |  | ||||||
|             primOp = primOp->primOpApp.left; |  | ||||||
|         } |  | ||||||
|         assert(primOp->type == tPrimOp); |  | ||||||
|         unsigned int arity = primOp->primOp->arity; |  | ||||||
|         unsigned int argsLeft = arity - argsDone; |  | ||||||
| 
 |  | ||||||
|         if (argsLeft == 1) { |  | ||||||
|             /* We have all the arguments, so call the primop. */ |  | ||||||
| 
 |  | ||||||
|             /* Put all the arguments in an array. */ |  | ||||||
|             Value * vArgs[arity]; |  | ||||||
|             unsigned int n = arity - 1; |  | ||||||
|             vArgs[n--] = &arg; |  | ||||||
|             for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left) |  | ||||||
|                 vArgs[n--] = arg->primOpApp.right; |  | ||||||
| 
 |  | ||||||
|             /* And call the primop. */ |  | ||||||
|             nrPrimOpCalls++; |  | ||||||
|             if (countCalls) primOpCalls[primOp->primOp->name]++; |  | ||||||
|             primOp->primOp->fun(*this, vArgs, v); |  | ||||||
|         } else { |  | ||||||
|             v.type = tPrimOpApp; |  | ||||||
|             v.primOpApp.left = allocValue(); |  | ||||||
|             *v.primOpApp.left = fun; |  | ||||||
|             v.primOpApp.right = &arg; |  | ||||||
|         } |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -772,7 +782,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) | ||||||
|             Bindings::iterator j = arg.attrs->find(i->name); |             Bindings::iterator j = arg.attrs->find(i->name); | ||||||
|             if (j == arg.attrs->end()) { |             if (j == arg.attrs->end()) { | ||||||
|                 if (!i->def) throwTypeError("%1% called without required argument `%2%'", |                 if (!i->def) throwTypeError("%1% called without required argument `%2%'", | ||||||
|                     fun.lambda.fun->showNamePos(), i->name); |                     *fun.lambda.fun, i->name); | ||||||
|                 env2.values[displ++] = i->def->maybeThunk(*this, env2); |                 env2.values[displ++] = i->def->maybeThunk(*this, env2); | ||||||
|             } else { |             } else { | ||||||
|                 attrsUsed++; |                 attrsUsed++; | ||||||
|  | @ -787,20 +797,32 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) | ||||||
|                user. */ |                user. */ | ||||||
|             foreach (Bindings::iterator, i, *arg.attrs) |             foreach (Bindings::iterator, i, *arg.attrs) | ||||||
|                 if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end()) |                 if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end()) | ||||||
|                     throwTypeError("%1% called with unexpected argument `%2%'", fun.lambda.fun->showNamePos(), i->name); |                     throwTypeError("%1% called with unexpected argument `%2%'", *fun.lambda.fun, i->name); | ||||||
|             abort(); // can't happen
 |             abort(); // can't happen
 | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nrFunctionCalls++; |     nrFunctionCalls++; | ||||||
|     if (countCalls) functionCalls[fun.lambda.fun]++; |     if (countCalls) incrFunctionCall(fun.lambda.fun); | ||||||
| 
 | 
 | ||||||
|  |     fun.lambda.fun->body->eval(*this, env2, v); | ||||||
|  | 
 | ||||||
|  | #if 0 | ||||||
|     try { |     try { | ||||||
|         fun.lambda.fun->body->eval(*this, env2, v); |         fun.lambda.fun->body->eval(*this, env2, v); | ||||||
|     } catch (Error & e) { |     } catch (Error & e) { | ||||||
|         addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos()); |         addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos()); | ||||||
|         throw; |         throw; | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Lifted out of callFunction() because it creates a temporary that
 | ||||||
|  | // prevents tail-call optimisation.
 | ||||||
|  | void EvalState::incrFunctionCall(ExprLambda * fun) | ||||||
|  | { | ||||||
|  |     functionCalls[fun]++; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -227,6 +227,7 @@ public: | ||||||
|     bool eqValues(Value & v1, Value & v2); |     bool eqValues(Value & v1, Value & v2); | ||||||
| 
 | 
 | ||||||
|     void callFunction(Value & fun, Value & arg, Value & v); |     void callFunction(Value & fun, Value & arg, Value & v); | ||||||
|  |     void callPrimOp(Value & fun, Value & arg, Value & v); | ||||||
| 
 | 
 | ||||||
|     /* Automatically call a function for which each argument has a
 |     /* Automatically call a function for which each argument has a
 | ||||||
|        default value or has a binding in the `args' map. */ |        default value or has a binding in the `args' map. */ | ||||||
|  | @ -268,6 +269,8 @@ private: | ||||||
|     typedef std::map<ExprLambda *, unsigned int> FunctionCalls; |     typedef std::map<ExprLambda *, unsigned int> FunctionCalls; | ||||||
|     FunctionCalls functionCalls; |     FunctionCalls functionCalls; | ||||||
| 
 | 
 | ||||||
|  |     void incrFunctionCall(ExprLambda * fun); | ||||||
|  | 
 | ||||||
|     typedef std::map<Pos, unsigned int> AttrSelects; |     typedef std::map<Pos, unsigned int> AttrSelects; | ||||||
|     AttrSelects attrSelects; |     AttrSelects attrSelects; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -330,7 +330,7 @@ void ExprLambda::setName(Symbol & name) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| string ExprLambda::showNamePos() | string ExprLambda::showNamePos() const | ||||||
| { | { | ||||||
|     return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str(); |     return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -205,7 +205,7 @@ struct ExprLambda : Expr | ||||||
|                 % arg % pos); |                 % arg % pos); | ||||||
|     }; |     }; | ||||||
|     void setName(Symbol & name); |     void setName(Symbol & name); | ||||||
|     string showNamePos(); |     string showNamePos() const; | ||||||
|     COMMON_METHODS |     COMMON_METHODS | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue