diff --git a/src/snc/analysis.c b/src/snc/analysis.c
index 1748fa2a83b37778b720baaeaebf67d6b4bf4e2c..01e4e10846eb66d2385cc9a12f2a4ce1083083db 100644
--- a/src/snc/analysis.c
+++ b/src/snc/analysis.c
@@ -24,6 +24,7 @@ in the file LICENSE that is included with this distribution.
 
 static const int impossible = 0;
 
+static void analyse_funcdefs(Expr *prog);
 static void analyse_definitions(Program *p);
 static void analyse_option(Options *options, Expr *defn);
 static void analyse_state_option(StateOptions *options, Expr *defn);
@@ -76,6 +77,7 @@ Program *analyse_program(Expr *prog, Options options)
 	report("created symbol table, channel list, and syncq list\n");
 #endif
 
+	analyse_funcdefs(p->prog);
 	analyse_definitions(p);
 	p->num_ss = connect_states(p->sym_table, prog);
 	connect_variables(p->sym_table, prog);
@@ -86,6 +88,52 @@ Program *analyse_program(Expr *prog, Options options)
 	return p;
 }
 
+static void analyse_funcdefs(Expr *prog)
+{
+	Expr *f;
+
+	assert(prog->type == D_PROG);
+	foreach(f, prog->prog_funcdefs)
+	{
+		Expr *d = f->funcdef_decl;
+		Var *var = d->extra.e_decl;
+		Expr *p;
+
+		assert(f->type == D_FUNCDEF);
+		if (var->type->tag != T_FUNCTION)
+		{
+			error_at_expr(d, "not a function type\n");
+			continue;
+		}
+		f->funcdef_params = var->type->val.function.param_decls;
+		assert(f->funcdef_params);	/* invariant enforced by syntax */
+		p = f->funcdef_params;
+		if (p->extra.e_decl->type->tag == T_VOID)
+		{
+			/* no other params should be there */
+			if (p->next)
+			{
+				error_at_expr(p->next, "void must be the only parameter\n");
+			}
+			if (p->extra.e_decl->name)
+			{
+				error_at_expr(p, "void parameter should not have a name\n");
+			}
+			/* void means empty parameter list */
+			f->funcdef_params = var->type->val.function.param_decls = 0;
+		}
+		foreach(p, f->funcdef_params)
+		{
+			/* check parameter has a name */
+			if (!p->extra.e_decl->name)
+			{
+				error_at_expr(p, "function parameter must have a name\n");
+			}
+		}
+		prog->prog_defns = link_expr(prog->prog_defns, d);
+	}
+}
+
 static int analyse_defn(Expr *scope, Expr *parent_scope, void *parg)
 {
 	Program	*p = (Program *)parg;
@@ -171,6 +219,8 @@ VarList **pvar_list_from_scope(Expr *scope)
 		return &scope->extra.e_state->var_list;
 	case S_CMPND:
 		return &scope->extra.e_cmpnd;
+	case D_FUNCDEF:
+		return &scope->extra.e_funcdef;
 	default:
 		assert(impossible); return NULL;
 	}
@@ -191,6 +241,8 @@ Expr *defn_list_from_scope(Expr *scope)
 		return scope->state_defns;
 	case S_CMPND:
 		return scope->cmpnd_defns;
+	case D_FUNCDEF:
+		return scope->funcdef_params;
 	default:
 		assert(impossible); return NULL;
 	}
@@ -305,6 +357,8 @@ static void analyse_declaration(SymTable st, Expr *scope, Expr *defn)
 
 	var_list = var_list_from_scope(scope);
 
+	assert(vp->name);
+
 	if (!sym_table_insert(st, vp->name, var_list, vp))
 	{
 		Var *vp2 = (Var *)sym_table_lookup(st, vp->name, var_list);
diff --git a/src/snc/gen_code.c b/src/snc/gen_code.c
index 843f2a10184b09a8170032da4d7a6b92cca314b4..64e5a217a3a5758cd641ff17537d2fdae8312b2d 100644
--- a/src/snc/gen_code.c
+++ b/src/snc/gen_code.c
@@ -152,7 +152,8 @@ static void gen_user_var(Program *p)
 	/* Convert internal type to `C' type */
 	foreach (vp, p->prog->extra.e_prog->first)
 	{
-		if (vp->decl && vp->type->tag != T_NONE && vp->type->tag != T_EVFLAG)
+		if (vp->decl && vp->type->tag != T_NONE && vp->type->tag != T_EVFLAG &&
+			vp->type->tag != T_FUNCTION)
 		{
 			gen_line_marker(vp->decl);
 			if (!opt_reent) gen_code("static");
@@ -220,6 +221,25 @@ static void gen_user_var(Program *p)
 		}
 		gen_code("};\n");
 	}
+	foreach (vp, p->prog->extra.e_prog->first)
+	{
+		if (vp->decl && vp->type->tag == T_FUNCTION)
+		{
+			Expr *param_decl;
+
+			gen_line_marker(vp->decl);
+			gen_code("static ");
+			gen_type(vp->type->val.function.return_type, "", vp->name);
+			gen_code("(SS_ID " NM_SS ", SEQ_VARS *const " NM_VARS_ARG);
+			foreach (param_decl, vp->type->val.function.param_decls)
+			{
+				Var *vp = param_decl->extra.e_decl;
+				gen_code(", ");
+				gen_var_decl(vp);
+			}
+			gen_code(");\n");
+		}
+	}
 	gen_code("\n");
 }
 
diff --git a/src/snc/gen_ss_code.c b/src/snc/gen_ss_code.c
index 75da64cab03c891eb64929f6aa9c039be215f7cb..7538ab419758c8446957528571fa7242c8acb297 100644
--- a/src/snc/gen_ss_code.c
+++ b/src/snc/gen_ss_code.c
@@ -62,6 +62,7 @@ static void gen_prog_entex_func(
 static void gen_prog_init_body(Expr *prog);
 static void gen_prog_entry_body(Expr *prog);
 static void gen_prog_exit_body(Expr *prog);
+static void gen_funcdef(Expr *fp);
 
 /*
  * Expression context. Certain nodes of the syntax tree are
@@ -74,7 +75,8 @@ enum expr_context
 	C_COND,		/* when() condition */
 	C_TRANS,	/* state transition actions */
 	C_SS,		/* otherwise inside a state set */
-	C_GLOBAL	/* outside state sets */
+	C_FUNC,		/* inside a function definition */
+	C_GLOBAL	/* outside state sets and functions */
 };
 
 /*
@@ -87,7 +89,7 @@ static Options global_options;
 /* Generate state set C code from analysed syntax tree */
 void gen_ss_code(Expr *prog, Options options)
 {
-	Expr	*sp, *ssp;
+	Expr	*sp, *ssp, *fp;
 	uint	ss_num = 0;
 
 	/* HACK: intialise global variable as implicit parameter */
@@ -135,6 +137,12 @@ void gen_ss_code(Expr *prog, Options options)
 	/* Generate program exit func */
 	if (prog->prog_exit)
 		gen_prog_entex_func(prog, "exit", NM_EXIT, gen_prog_exit_body);
+
+	/* Generate function definitions */
+	foreach(fp, prog->prog_funcdefs)
+	{
+		gen_funcdef(fp);
+	}
 }
 
 /* Generate a local C variable declaration for each variable declared
@@ -304,7 +312,7 @@ static void gen_var_access(Var *vp)
 	{
 		gen_code("%d/*%s*/", vp->chan.evflag->index, vp->name);
 	}
-	else if (vp->type->tag == T_NONE)
+	else if (vp->type->tag == T_NONE || vp->type->tag == T_FUNCTION)
 	{
 		gen_code("%s", vp->name);
 	}
@@ -407,6 +415,15 @@ static void gen_expr(
 		indent(level);
 		gen_code("{*" NM_PNST " = %d; return;}\n", ep->extra.e_change->extra.e_state->index);
 		break;
+	case S_RETURN:
+		if (context != C_FUNC)
+		{
+			error_at_expr(ep, "return statement not allowed here\n");
+			break;
+		}
+		indent(level);
+		gen_code("return "); gen_expr(context, ep->return_expr, level); gen_code(";\n");
+		break;
 	/* Expressions */
 	case E_VAR:
 		gen_var_access(ep->extra.e_var);
@@ -434,6 +451,13 @@ static void gen_expr(
 		}
 		gen_expr(context, ep->func_expr, 0);
 		gen_code("(");
+		if (ep->func_expr->type == E_VAR && ep->func_expr->extra.e_var->type->tag == T_FUNCTION)
+		{
+			/* direct call and not a foreign function => add implicit parameters */
+			gen_code(NM_SS ", " NM_VARS_ARG);
+			if (ep->func_args)
+				gen_code(", ");
+		}
 		foreach (cep, ep->func_args)
 		{
 			gen_expr(context, cep, 0);
@@ -881,3 +905,23 @@ static void gen_prog_exit_body(Expr *prog)
 	assert(prog->type == D_PROG);
 	gen_entex_body(prog->prog_exit, C_SS);
 }
+
+static void gen_funcdef(Expr *fp)
+{
+	Expr *param_decl;
+	Var *vp = fp->funcdef_decl->extra.e_decl;
+
+	assert(fp->type == D_FUNCDEF);
+	gen_line_marker(vp->decl);
+	gen_code("static ");
+	gen_type(vp->type->val.function.return_type, "", vp->name);
+	gen_code("(SS_ID " NM_SS ", SEQ_VARS *const " NM_VARS_ARG);
+	foreach (param_decl,  fp->funcdef_params)
+	{
+		vp = param_decl->extra.e_decl;
+		gen_code(", ");
+		gen_var_decl(vp);
+	}
+	gen_code(")\n");
+	gen_block(fp->funcdef_block, C_FUNC, 0);
+}
diff --git a/src/snc/snl.lem b/src/snc/snl.lem
index d244388864a4fc264df291bb8e96e3c576a8d5ad..7e017f307f293308d718d9a617190fcb2278ee9c 100644
--- a/src/snc/snl.lem
+++ b/src/snc/snl.lem
@@ -92,9 +92,10 @@ program ::=
 	entry(en)
 	state_sets(ss)
 	exit(ex)
+        functions(fs)
 	c_codes(cc).
 {
-	*presult =  expr(D_PROG, n, pp, ds, en, ss, ex, cc);
+	*presult =  expr(D_PROG, n, pp, ds, en, ss, ex, fs, cc);
 }
 
 program_param(p) ::= LPAREN string(x) RPAREN.	{ p = x; }
@@ -359,6 +360,8 @@ statements(p) ::= .				{ p = 0; }
 
 statement(p) ::= BREAK(t) SEMICOLON.		{ p = expr(S_JUMP, t); }
 statement(p) ::= CONTINUE(t) SEMICOLON.		{ p = expr(S_JUMP, t); }
+statement(p) ::= RETURN(t) opt_expr(x) SEMICOLON.
+						{ p = expr(S_RETURN, t, x); }
 statement(p) ::= STATE NAME(t) SEMICOLON.	{ p = expr(S_CHANGE, t); }
 statement(p) ::= c_code(x).			{ p = x; }
 statement(p) ::= block(x).			{ p = x; }
@@ -459,7 +462,7 @@ opt_expr(p) ::= comma_expr(x).			{ p = x; }
 opt_expr(p) ::= .				{ p = 0; }
 
 // Function arguments
-// Ssyntactically the same as opt_expr but interpreted differently.
+// Syntactically the same as opt_expr but interpreted differently.
 args(p) ::= args(xs) COMMA expr(x).		{ p = link_expr(xs, x); }
 args(p) ::= expr(x).				{ p = x; }
 args(p) ::= .					{ p = 0; }
@@ -468,6 +471,15 @@ string(p) ::= STRCON(t).			{ p = expr(E_STRING, t); }
 
 member(p) ::= NAME(t).				{ p = expr(E_MEMBER, t); }
 
+// Functions
+
+functions(p) ::= .					{ p = 0; }
+functions(p) ::= functions(fs) function(f).		{ p = link_expr(fs, f); }
+
+function(p) ::= FUNCTION(l) basetype(t) declarator(d) block(b). {
+	p = expr(D_FUNCDEF, l, decl_add_base_type(d, t), 0, b);
+}
+
 // Literal (C) code
 
 c_codes(p) ::= c_codes(xs) c_code(x).		{ p = link_expr(xs, x); }
diff --git a/src/snc/snl.re b/src/snc/snl.re
index 9c6898c36dbef53a3d1f9f41663b4de9d986ddcb..267b7d9505f84dca5c490fa756cccabc1329dae0 100644
--- a/src/snc/snl.re
+++ b/src/snc/snl.re
@@ -244,12 +244,14 @@ snl:
 	"float"		{ TYPEWORD(FLOAT,	"float"); }
 	"for"		{ KEYWORD(FOR,		"for"); }
 	"foreign"	{ TYPEWORD(FOREIGN,	"foreign"); }
+	"function"	{ TYPEWORD(FUNCTION,	"function"); }
 	"if"		{ KEYWORD(IF,		"if"); }
 	"int"		{ TYPEWORD(INT,		"int"); }
 	"long"		{ TYPEWORD(LONG,	"long"); }
 	"monitor"	{ KEYWORD(MONITOR,	"monitor"); }
 	"option"	{ KEYWORD(OPTION,	"option"); }
 	"program"	{ KEYWORD(PROGRAM,	"program"); }
+	"return"	{ KEYWORD(RETURN,	"return"); }
 	"short"		{ TYPEWORD(SHORT,	"short"); }
 	"sizeof"	{ TYPEWORD(SIZEOF,	"sizeof"); }
 	"ss"		{ KEYWORD(SS,		"ss"); }
diff --git a/src/snc/types.h b/src/snc/types.h
index 831151a04003fe17e48de80918371430103078c5..ebedc9e2c303ae246ef0e647531f48d6532577cd 100644
--- a/src/snc/types.h
+++ b/src/snc/types.h
@@ -123,14 +123,15 @@ struct expression			/* generic syntax node */
 		Var	*e_var;		/* variable reference */
 		Var	*e_decl;	/* variable declaration */
 		uint	e_option;	/* option value (1 or 0) */
-		VarList	*e_prog;	/* top-level definitions */
+		VarList	*e_prog;	/* top-level declarations */
 		StateSet *e_ss;		/* state set data */
 		State	*e_state;	/* state data */
 		When	*e_when;	/* transition data */
 		Expr	*e_change;	/* declaration of target state */
-		VarList	*e_cmpnd;	/* block local definitions */
+		VarList	*e_cmpnd;	/* block local declarations */
 		FuncSym	*e_builtin;	/* builtin function */
 		ConstSym *e_const;	/* builtin constant */
+		VarList *e_funcdef;	/* parameter declarations */
 	}	extra;
 };
 
@@ -238,12 +239,12 @@ struct program
 
 /* Expression types that are scopes. By definition, a scope is an expression
    that allows variable declarations as (immediate) subexpressions. */
-#define scope_mask		( bit(D_PROG)    | bit(D_SS)      | bit(D_STATE)  | bit(S_CMPND) )
+#define scope_mask		( bit(D_PROG) | bit(D_FUNCDEF) | bit(D_SS) | bit(D_STATE) | bit(S_CMPND) )
 /* Whether an expression is a scope */
 #define is_scope(e)		((bit((e)->type) & scope_mask) != 0)
 
 /* Expression types that may have sub-scopes */
-#define has_sub_scope_mask	( bit(D_ENTEX)   | bit(D_PROG)    | bit(D_SS)\
+#define has_sub_scope_mask	( bit(D_ENTEX)   | bit(D_FUNCDEF) | bit(D_PROG)   | bit(D_SS)\
 				| bit(D_STATE)   | bit(D_WHEN)    | bit(S_CMPND)  | bit(S_FOR)\
 				| bit(S_IF)      | bit(S_STMT)    | bit(S_WHILE) )
 /* Expression types that are actually expressions i.e. no definitions or statements.
@@ -270,9 +271,10 @@ enum expr_type			/* description [child expressions...] */
 	D_ASSIGN,		/* assign statement [subscr,pvs] */
 	D_DECL,			/* variable declaration [init] */
 	D_ENTEX,		/* entry or exit statement [block] */
+	D_FUNCDEF,		/* function definition [decl,params,block] */
 	D_MONITOR,		/* monitor statement [subscr] */
 	D_OPTION,		/* option definition [] */
-	D_PROG,			/* whole program [param,defns,entry,statesets,exit,ccode] */
+	D_PROG,			/* whole program [param,defns,entry,statesets,exit,funcdefs,ccode] */
 	D_SS,			/* state set statement [defns,states] */
 	D_STATE,		/* state statement [defns,entry,whens,exit] */
 	D_SYNC,			/* sync statement [subscr,evflag] */
@@ -300,6 +302,7 @@ enum expr_type			/* description [child expressions...] */
 	S_FOR,			/* for statement [init,cond,iter,stmt] */
 	S_IF,			/* if statement [cond,then,else] */
 	S_JUMP,			/* break or continue stmt [] */
+	S_RETURN,		/* return stmt [expr] */
 	S_STMT,			/* simple statement, i.e. 'expr;'  [expr] */
 	S_WHILE,		/* while statement [cond,stmt] */
 
@@ -330,6 +333,9 @@ STATIC_ASSERT(NUM_EXPR_TYPES <= 8*sizeof(TypeMask));
 #define for_stmt	children[3]
 #define func_expr	children[0]
 #define func_args	children[1]
+#define funcdef_decl	children[0]
+#define funcdef_params	children[1]
+#define funcdef_block	children[2]
 #define if_cond		children[0]
 #define if_then		children[1]
 #define if_else		children[2]
@@ -343,7 +349,9 @@ STATIC_ASSERT(NUM_EXPR_TYPES <= 8*sizeof(TypeMask));
 #define prog_entry	children[2]
 #define prog_statesets	children[3]
 #define prog_exit	children[4]
-#define prog_ccode	children[5]
+#define prog_funcdefs	children[5]
+#define prog_ccode	children[6]
+#define return_expr	children[0]
 #define select_left	children[0]
 #define select_right	children[1]
 #define ss_defns	children[0]
@@ -368,12 +376,6 @@ STATIC_ASSERT(NUM_EXPR_TYPES <= 8*sizeof(TypeMask));
 #define while_cond	children[0]
 #define while_stmt	children[1]
 
-/* Accessors for var_lists (only for scopes) */
-#define prog_var_list	extra.e_prog
-#define ss_var_list	extra.e_ss->var_list
-#define state_var_list	extra.e_state->var_list
-#define cmpnd_var_list	extra.e_cmpnd;
-
 #ifndef expr_type_GLOBAL
 extern
 #endif
@@ -388,9 +390,10 @@ expr_type_info[]
 	{ "D_ASSIGN",	2 },
 	{ "D_DECL",	1 },
 	{ "D_ENTEX",	1 },
+	{ "D_FUNCDEF",	3 },
 	{ "D_MONITOR",	1 },
 	{ "D_OPTION",	0 },
-	{ "D_PROG",	6 },
+	{ "D_PROG",	7 },
 	{ "D_SS",	2 },
 	{ "D_STATE",	4 },
 	{ "D_SYNC",	2 },
@@ -416,6 +419,7 @@ expr_type_info[]
 	{ "S_FOR",	4 },
 	{ "S_IF",	3 },
 	{ "S_JUMP",	0 },
+	{ "S_RETURN",	1 },
 	{ "S_STMT",	1 },
 	{ "S_WHILE",	2 },
 	{ "T_TEXT",	0 },
diff --git a/src/snc/var_types.c b/src/snc/var_types.c
index f6b8c837ac2ff1144e34f6e26f6da121d0ef903c..3c2cb6e1754a16d9b2b86b13c679803a6f96b815 100644
--- a/src/snc/var_types.c
+++ b/src/snc/var_types.c
@@ -60,7 +60,7 @@ Expr *decl_add_base_type(Expr *ds, Type basetype)
                     error_at_expr(d, "cannot declare function returning foreign variable\n");
                 }
                 if (basetype.tag == T_EVFLAG) {
-                    error_at_expr(d, "cannot declare returning event flag\n");
+                    error_at_expr(d, "cannot declare function returning event flag\n");
                 }
                 t->parent->val.function.return_type = t;
                 break;
@@ -135,8 +135,6 @@ Expr *decl_postfix_array(Expr *d, char *s)
 Expr *decl_postfix_function(Expr *decl, Expr *param_decls)
 {
     Type *t = new(Type);
-    Expr *param_decl;
-    unsigned n = 0;
 
     assert(decl->type == D_DECL);          /* pre-condition */
 
@@ -145,18 +143,7 @@ Expr *decl_postfix_function(Expr *decl, Expr *param_decls)
 #endif
 
     t->tag = T_FUNCTION;
-    foreach (param_decl, param_decls) {
-        n++;
-    }
-    t->val.function.num_params = n;
-    t->val.function.param_types = newArray(Type*, n);
-    n = 0;
-    foreach (param_decl, param_decls) {
-        assert(param_decl->type == D_DECL);   /* pre-condition */
-        assert(param_decl->extra.e_decl);     /* pre-condition */
-        t->val.function.param_types[n] = param_decl->extra.e_decl->type;
-        n++;
-    }
+    t->val.function.param_decls = param_decls;
     t->parent = decl->extra.e_decl->type;
     decl->extra.e_decl->type = t;
     return decl;
@@ -280,7 +267,7 @@ unsigned type_assignable(Type *t)
 static void gen_array_pointer(Type *t, enum type_tag last_tag, const char *prefix, const char *name)
 {
     int paren = last_tag == T_ARRAY || last_tag == T_FUNCTION;
-    int i;
+    Expr *pd;
 
     switch (t->tag) {
     case T_POINTER:
@@ -298,10 +285,11 @@ static void gen_array_pointer(Type *t, enum type_tag last_tag, const char *prefi
     case T_FUNCTION:
         gen_array_pointer(t->parent, t->tag, prefix, name);
         gen_code("(");
-        for (i = 0; i < t->val.function.num_params; i++) {
-            if (i > 0)
+        foreach (pd, t->val.function.param_decls) {
+            Var *var = pd->extra.e_decl;
+            gen_type(var->type, "", var->name);
+            if (pd->next)
                 gen_code (", ");
-            gen_type(t->val.function.param_types[i], 0, 0);
         }
         gen_code(")");
         break;
@@ -315,6 +303,12 @@ static void gen_array_pointer(Type *t, enum type_tag last_tag, const char *prefi
 void gen_type(Type *t, const char *prefix, const char *name)
 {
     Type *bt = base_type(t);
+    Type *saved_bt = bt;
+
+    /* DIRTY HACK: overwrite parent pointer so we can use this for the return type of a function */
+    if (bt->tag == T_FUNCTION) {
+        bt = t->parent = bt->parent;
+    }
 
     switch (bt->tag) {
     case T_EVFLAG:
@@ -333,4 +327,6 @@ void gen_type(Type *t, const char *prefix, const char *name)
         assert(impossible);
     }
     gen_array_pointer(bt->parent, T_NONE, prefix, name);
+    /* DIRTY HACK: restore parent pointer */
+    t->parent = saved_bt;
 }
diff --git a/src/snc/var_types.h b/src/snc/var_types.h
index 1f2188b0d7b24b87dc111800ff2c1dfa7580c0fa..ba48b05f7662feb6f43bfbfacc14260d68e37812 100644
--- a/src/snc/var_types.h
+++ b/src/snc/var_types.h
@@ -28,7 +28,7 @@ enum foreign_type_tag {
 };
 
 struct array_type {
-    unsigned    num_elems;
+    unsigned num_elems;
     struct type *elem_type;
 };
 
@@ -37,8 +37,7 @@ struct pointer_type {
 };
 
 struct function_type {
-    unsigned    num_params;
-    struct type **param_types;  /* array of pointers */
+    struct expression *param_decls;
     struct type *return_type;
 };
 
diff --git a/test/compiler/Makefile b/test/compiler/Makefile
index 99356d88bcc56898be61b6281a1d7864f414e8ba..a09e4d8883ef92515cd2ea092618a5981401a71a 100644
--- a/test/compiler/Makefile
+++ b/test/compiler/Makefile
@@ -20,6 +20,8 @@ TESTSCRIPTS_HOST += snc_test.t
 #TESTSCRIPTS_HOST += build64_test.t
 #TESTSCRIPTS_CROSS += build64_test.t
 
+TESTPROD_HOST += funcdef
+
 PROD_LIBS += seq pv
 PROD_LIBS += $(EPICS_BASE_IOC_LIBS)
 
diff --git a/test/compiler/funcdef.st b/test/compiler/funcdef.st
new file mode 100644
index 0000000000000000000000000000000000000000..97d17b504d35b648c705e2622f476438330b0d84
--- /dev/null
+++ b/test/compiler/funcdef.st
@@ -0,0 +1,33 @@
+program funcdefTest
+
+option +r;
+
+int x;
+assign x;
+
+ss simple {
+    state simple {
+        when () {
+            void *p = f1();
+            p = f2(p,0);
+            pvSetX(1.0);
+        } exit
+    }
+}
+
+function void *f1(void)
+{
+    return 0;
+}
+
+function void *f2(void *x, double **d)
+{
+    void *(*f)(void) = (void *(*)(void))x;
+    return f();
+}
+
+function void pvSetX(int val)
+{
+    pvPut(x,val);
+    printf("x=%d, val=%d\n", x, val);
+}