1/*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source.  A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
12 *
13 * CDDL HEADER END
14 */
15
16/*
17 * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
18 */
19
20/*
21 * Syntactic sugar features are implemented by transforming the D parse tree
22 * such that it only uses the subset of D that is supported by the rest of the
23 * compiler / the kernel.  A clause containing these language features is
24 * referred to as a "super-clause", and its transformation typically entails
25 * creating several "sub-clauses" to implement it. For diagnosability, the
26 * sub-clauses will be printed if the "-xtree=8" flag is specified.
27 *
28 * Currently, the only syntactic sugar feature is "if/else" statements.  Each
29 * basic block (e.g. the body of the "if" and "else" statements, and the
30 * statements before and after) is turned into its own sub-clause, with a
31 * predicate that causes it to be executed only if the code flows to this point.
32 * Nested if/else statements are supported.
33 *
34 * This infrastructure is designed to accommodate other syntactic sugar features
35 * in the future.
36 */
37
38#include <sys/types.h>
39#include <sys/wait.h>
40#include <sys/sysmacros.h>
41
42#include <assert.h>
43#include <strings.h>
44#include <stdlib.h>
45#include <stdio.h>
46#include <ctype.h>
47#include <dt_module.h>
48#include <dt_program.h>
49#include <dt_provider.h>
50#include <dt_printf.h>
51#include <dt_pid.h>
52#include <dt_grammar.h>
53#include <dt_ident.h>
54#include <dt_string.h>
55#include <dt_impl.h>
56
57typedef struct dt_sugar_parse {
58	dtrace_hdl_t *dtsp_dtp;		/* dtrace handle */
59	dt_node_t *dtsp_pdescs;		/* probe descriptions */
60	int dtsp_num_conditions;	/* number of condition variables */
61	int dtsp_num_ifs;		/* number of "if" statements */
62	dt_node_t *dtsp_clause_list;	/* list of clauses */
63} dt_sugar_parse_t;
64
65static void dt_sugar_visit_stmts(dt_sugar_parse_t *, dt_node_t *, int);
66
67/*
68 * Return a node for "self->%error".
69 *
70 * Note that the "%" is part of the variable name, and is included so that
71 * this variable name can not collide with any user-specified variable.
72 *
73 * This error variable is used to keep track of if there has been an error
74 * in any of the sub-clauses, and is used to prevent execution of subsequent
75 * sub-clauses following an error.
76 */
77static dt_node_t *
78dt_sugar_new_error_var(void)
79{
80	return (dt_node_op2(DT_TOK_PTR, dt_node_ident(strdup("self")),
81	    dt_node_ident(strdup("%error"))));
82}
83
84/*
85 * Append this clause to the clause list.
86 */
87static void
88dt_sugar_append_clause(dt_sugar_parse_t *dp, dt_node_t *clause)
89{
90	dp->dtsp_clause_list = dt_node_link(dp->dtsp_clause_list, clause);
91}
92
93/*
94 * Prepend this clause to the clause list.
95 */
96static void
97dt_sugar_prepend_clause(dt_sugar_parse_t *dp, dt_node_t *clause)
98{
99	dp->dtsp_clause_list = dt_node_link(clause, dp->dtsp_clause_list);
100}
101
102/*
103 * Return a node for "this->%condition_<condid>", or NULL if condid==0.
104 *
105 * Note that the "%" is part of the variable name, and is included so that
106 * this variable name can not collide with any user-specified variable.
107 */
108static dt_node_t *
109dt_sugar_new_condition_var(int condid)
110{
111	char *str;
112
113	if (condid == 0)
114		return (NULL);
115	assert(condid > 0);
116
117	(void) asprintf(&str, "%%condition_%d", ABS(condid));
118	return (dt_node_op2(DT_TOK_PTR, dt_node_ident(strdup("this")),
119	    dt_node_ident(str)));
120}
121
122/*
123 * Return new clause to evaluate predicate and set newcond.  condid is
124 * the condition that we are already under, or 0 if none.
125 * The new clause will be of the form:
126 *
127 * dp_pdescs
128 * /!self->%error/
129 * {
130 *	this->%condition_<newcond> =
131 *	    (this->%condition_<condid> && pred);
132 * }
133 *
134 * Note: if condid==0, we will instead do "... = (1 && pred)", to effectively
135 * convert the pred to a boolean.
136 *
137 * Note: Unless an error has been encountered, we always set the condition
138 * variable (either to 0 or 1).  This lets us avoid resetting the condition
139 * variables back to 0 when the super-clause completes.
140 */
141static dt_node_t *
142dt_sugar_new_condition_impl(dt_sugar_parse_t *dp,
143    dt_node_t *pred, int condid, int newcond)
144{
145	dt_node_t *value, *body, *newpred;
146
147	/* predicate is !self->%error */
148	newpred = dt_node_op1(DT_TOK_LNEG, dt_sugar_new_error_var());
149
150	if (condid == 0) {
151		/*
152		 * value is (1 && pred)
153		 *
154		 * Note, D doesn't allow a probe-local "this" variable to
155		 * be reused as a different type, even from a different probe.
156		 * Therefore, value can't simply be <pred>, because then
157		 * its type could be different when we reuse this condid
158		 * in a different meta-clause.
159		 */
160		value = dt_node_op2(DT_TOK_LAND, dt_node_int(1), pred);
161	} else {
162		/* value is (this->%condition_<condid> && pred) */
163		value = dt_node_op2(DT_TOK_LAND,
164		    dt_sugar_new_condition_var(condid), pred);
165	}
166
167	/* body is "this->%condition_<retval> = <value>;" */
168	body = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
169	    dt_sugar_new_condition_var(newcond), value));
170
171	return (dt_node_clause(dp->dtsp_pdescs, newpred, body));
172}
173
174/*
175 * Generate a new clause to evaluate predicate and set a new condition variable,
176 * whose ID will be returned.  The new clause will be appended to
177 * dp_first_new_clause.
178 */
179static int
180dt_sugar_new_condition(dt_sugar_parse_t *dp, dt_node_t *pred, int condid)
181{
182	dp->dtsp_num_conditions++;
183	dt_sugar_append_clause(dp, dt_sugar_new_condition_impl(dp,
184	    pred, condid, dp->dtsp_num_conditions));
185	return (dp->dtsp_num_conditions);
186}
187
188/*
189 * Visit the specified node and all of its descendants.  Currently this is only
190 * used to count the number of "if" statements (dtsp_num_ifs).
191 */
192static void
193dt_sugar_visit_all(dt_sugar_parse_t *dp, dt_node_t *dnp)
194{
195	dt_node_t *arg;
196
197	switch (dnp->dn_kind) {
198	case DT_NODE_FREE:
199	case DT_NODE_INT:
200	case DT_NODE_STRING:
201	case DT_NODE_SYM:
202	case DT_NODE_TYPE:
203	case DT_NODE_PROBE:
204	case DT_NODE_PDESC:
205	case DT_NODE_IDENT:
206		break;
207
208	case DT_NODE_FUNC:
209		for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list)
210			dt_sugar_visit_all(dp, arg);
211		break;
212
213	case DT_NODE_OP1:
214		dt_sugar_visit_all(dp, dnp->dn_child);
215		break;
216
217	case DT_NODE_OP2:
218		dt_sugar_visit_all(dp, dnp->dn_left);
219		dt_sugar_visit_all(dp, dnp->dn_right);
220		if (dnp->dn_op == DT_TOK_LBRAC) {
221			dt_node_t *ln = dnp->dn_right;
222			while (ln->dn_list != NULL) {
223				dt_sugar_visit_all(dp, ln->dn_list);
224				ln = ln->dn_list;
225			}
226		}
227		break;
228
229	case DT_NODE_OP3:
230		dt_sugar_visit_all(dp, dnp->dn_expr);
231		dt_sugar_visit_all(dp, dnp->dn_left);
232		dt_sugar_visit_all(dp, dnp->dn_right);
233		break;
234
235	case DT_NODE_DEXPR:
236	case DT_NODE_DFUNC:
237		dt_sugar_visit_all(dp, dnp->dn_expr);
238		break;
239
240	case DT_NODE_AGG:
241		for (arg = dnp->dn_aggtup; arg != NULL; arg = arg->dn_list)
242			dt_sugar_visit_all(dp, arg);
243
244		if (dnp->dn_aggfun)
245			dt_sugar_visit_all(dp, dnp->dn_aggfun);
246		break;
247
248	case DT_NODE_CLAUSE:
249		for (arg = dnp->dn_pdescs; arg != NULL; arg = arg->dn_list)
250			dt_sugar_visit_all(dp, arg);
251
252		if (dnp->dn_pred != NULL)
253			dt_sugar_visit_all(dp, dnp->dn_pred);
254
255		for (arg = dnp->dn_acts; arg != NULL; arg = arg->dn_list)
256			dt_sugar_visit_all(dp, arg);
257		break;
258
259	case DT_NODE_INLINE: {
260		const dt_idnode_t *inp = dnp->dn_ident->di_iarg;
261
262		dt_sugar_visit_all(dp, inp->din_root);
263		break;
264	}
265	case DT_NODE_MEMBER:
266		if (dnp->dn_membexpr)
267			dt_sugar_visit_all(dp, dnp->dn_membexpr);
268		break;
269
270	case DT_NODE_XLATOR:
271		for (arg = dnp->dn_members; arg != NULL; arg = arg->dn_list)
272			dt_sugar_visit_all(dp, arg);
273		break;
274
275	case DT_NODE_PROVIDER:
276		for (arg = dnp->dn_probes; arg != NULL; arg = arg->dn_list)
277			dt_sugar_visit_all(dp, arg);
278		break;
279
280	case DT_NODE_PROG:
281		for (arg = dnp->dn_list; arg != NULL; arg = arg->dn_list)
282			dt_sugar_visit_all(dp, arg);
283		break;
284
285	case DT_NODE_IF:
286		dp->dtsp_num_ifs++;
287		dt_sugar_visit_all(dp, dnp->dn_conditional);
288
289		for (arg = dnp->dn_body; arg != NULL; arg = arg->dn_list)
290			dt_sugar_visit_all(dp, arg);
291		for (arg = dnp->dn_alternate_body; arg != NULL;
292		    arg = arg->dn_list)
293			dt_sugar_visit_all(dp, arg);
294
295		break;
296
297	default:
298		(void) dnerror(dnp, D_UNKNOWN, "bad node %p, kind %d\n",
299		    (void *)dnp, dnp->dn_kind);
300	}
301}
302
303/*
304 * Return a new clause which resets the error variable to zero:
305 *
306 *   dp_pdescs{ self->%error = 0; }
307 *
308 * This clause will be executed at the beginning of each meta-clause, to
309 * ensure the error variable is unset (in case the previous meta-clause
310 * failed).
311 */
312static dt_node_t *
313dt_sugar_new_clearerror_clause(dt_sugar_parse_t *dp)
314{
315	dt_node_t *stmt = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
316	    dt_sugar_new_error_var(), dt_node_int(0)));
317	return (dt_node_clause(dp->dtsp_pdescs, NULL, stmt));
318}
319
320/*
321 * Evaluate the conditional, and recursively visit the body of the "if"
322 * statement (and the "else", if present).
323 */
324static void
325dt_sugar_do_if(dt_sugar_parse_t *dp, dt_node_t *if_stmt, int precondition)
326{
327	int newid;
328
329	assert(if_stmt->dn_kind == DT_NODE_IF);
330
331	/* condition */
332	newid = dt_sugar_new_condition(dp,
333	    if_stmt->dn_conditional, precondition);
334
335	/* body of if */
336	dt_sugar_visit_stmts(dp, if_stmt->dn_body, newid);
337
338	/*
339	 * Visit the body of the "else" statement, if present.  Note that we
340	 * generate a new condition which is the inverse of the previous
341	 * condition.
342	 */
343	if (if_stmt->dn_alternate_body != NULL) {
344		dt_node_t *pred =
345		    dt_node_op1(DT_TOK_LNEG, dt_sugar_new_condition_var(newid));
346		dt_sugar_visit_stmts(dp, if_stmt->dn_alternate_body,
347		    dt_sugar_new_condition(dp, pred, precondition));
348	}
349}
350
351/*
352 * Generate a new clause to evaluate the statements based on the condition.
353 * The new clause will be appended to dp_first_new_clause.
354 *
355 * dp_pdescs
356 * /!self->%error && this->%condition_<condid>/
357 * {
358 *	stmts
359 * }
360 */
361static void
362dt_sugar_new_basic_block(dt_sugar_parse_t *dp, int condid, dt_node_t *stmts)
363{
364	dt_node_t *pred = NULL;
365
366	if (condid == 0) {
367		/*
368		 * Don't bother with !error on the first clause, because if
369		 * there is only one clause, we don't add the prelude to
370		 * zero out %error.
371		 */
372		if (dp->dtsp_num_conditions != 0) {
373			pred = dt_node_op1(DT_TOK_LNEG,
374			    dt_sugar_new_error_var());
375		}
376	} else {
377		pred = dt_node_op2(DT_TOK_LAND,
378		    dt_node_op1(DT_TOK_LNEG, dt_sugar_new_error_var()),
379		    dt_sugar_new_condition_var(condid));
380	}
381	dt_sugar_append_clause(dp,
382	    dt_node_clause(dp->dtsp_pdescs, pred, stmts));
383}
384
385/*
386 * Visit all the statements in this list, and break them into basic blocks,
387 * generating new clauses for "if" and "else" statements.
388 */
389static void
390dt_sugar_visit_stmts(dt_sugar_parse_t *dp, dt_node_t *stmts, int precondition)
391{
392	dt_node_t *stmt;
393	dt_node_t *prev_stmt = NULL;
394	dt_node_t *next_stmt;
395	dt_node_t *first_stmt_in_basic_block = NULL;
396
397	for (stmt = stmts; stmt != NULL; stmt = next_stmt) {
398		next_stmt = stmt->dn_list;
399
400		if (stmt->dn_kind != DT_NODE_IF) {
401			if (first_stmt_in_basic_block == NULL)
402				first_stmt_in_basic_block = stmt;
403			prev_stmt = stmt;
404			continue;
405		}
406
407		/*
408		 * Remove this and following statements from the previous
409		 * clause.
410		 */
411		if (prev_stmt != NULL)
412			prev_stmt->dn_list = NULL;
413
414		/*
415		 * Generate clause for statements preceding the "if"
416		 */
417		if (first_stmt_in_basic_block != NULL) {
418			dt_sugar_new_basic_block(dp, precondition,
419			    first_stmt_in_basic_block);
420		}
421
422		dt_sugar_do_if(dp, stmt, precondition);
423
424		first_stmt_in_basic_block = NULL;
425
426		prev_stmt = stmt;
427	}
428
429	/* generate clause for statements after last "if". */
430	if (first_stmt_in_basic_block != NULL) {
431		dt_sugar_new_basic_block(dp, precondition,
432		    first_stmt_in_basic_block);
433	}
434}
435
436/*
437 * Generate a new clause which will set the error variable when an error occurs.
438 * Only one of these clauses is created per program (e.g. script file).
439 * The clause is:
440 *
441 * dtrace:::ERROR{ self->%error = 1; }
442 */
443static dt_node_t *
444dt_sugar_makeerrorclause(void)
445{
446	dt_node_t *acts, *pdesc;
447
448	pdesc = dt_node_pdesc_by_name(strdup("dtrace:::ERROR"));
449
450	acts = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
451	    dt_sugar_new_error_var(), dt_node_int(1)));
452
453	return (dt_node_clause(pdesc, NULL, acts));
454}
455
456/*
457 * Transform the super-clause into straight-D, returning the new list of
458 * sub-clauses.
459 */
460dt_node_t *
461dt_compile_sugar(dtrace_hdl_t *dtp, dt_node_t *clause)
462{
463	dt_sugar_parse_t dp = { 0 };
464	int condid = 0;
465
466	dp.dtsp_dtp = dtp;
467	dp.dtsp_pdescs = clause->dn_pdescs;
468
469	/* make dt_node_int() generate an "int"-typed integer */
470	yyintdecimal = B_TRUE;
471	yyintsuffix[0] = '\0';
472	yyintprefix = 0;
473
474	dt_sugar_visit_all(&dp, clause);
475
476	if (dp.dtsp_num_ifs == 0 && dp.dtsp_num_conditions == 0) {
477		/*
478		 * There is nothing that modifies the number of clauses.  Use
479		 * the existing clause as-is, with its predicate intact.  This
480		 * ensures that in the absence of D sugar, the body of the
481		 * clause can create a variable that is referenced in the
482		 * predicate.
483		 */
484		dt_sugar_append_clause(&dp, dt_node_clause(clause->dn_pdescs,
485		    clause->dn_pred, clause->dn_acts));
486	} else {
487		if (clause->dn_pred != NULL) {
488			condid = dt_sugar_new_condition(&dp,
489			    clause->dn_pred, condid);
490		}
491
492		if (clause->dn_acts == NULL) {
493			/*
494			 * dt_sugar_visit_stmts() does not emit a clause with
495			 * an empty body (e.g. if there's an empty "if" body),
496			 * but we need the empty body here so that we
497			 * continue to get the default tracing action.
498			 */
499			dt_sugar_new_basic_block(&dp, condid, NULL);
500		} else {
501			dt_sugar_visit_stmts(&dp, clause->dn_acts, condid);
502		}
503	}
504
505	if (dp.dtsp_num_conditions != 0) {
506		dt_sugar_prepend_clause(&dp,
507		    dt_sugar_new_clearerror_clause(&dp));
508	}
509
510	if (dp.dtsp_clause_list != NULL &&
511	    dp.dtsp_clause_list->dn_list != NULL && !dtp->dt_has_sugar) {
512		dtp->dt_has_sugar = B_TRUE;
513		dt_sugar_prepend_clause(&dp, dt_sugar_makeerrorclause());
514	}
515	return (dp.dtsp_clause_list);
516}
517