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
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.
17 * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
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.
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.
34 * This infrastructure is designed to accommodate other syntactic sugar features
38 #include <sys/types.h>
40 #include <sys/sysmacros.h>
47 #include <dt_module.h>
48 #include <dt_program.h>
49 #include <dt_provider.h>
50 #include <dt_printf.h>
52 #include <dt_grammar.h>
54 #include <dt_string.h>
57 typedef 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 */
65 static void dt_sugar_visit_stmts(dt_sugar_parse_t
*, dt_node_t
*, int);
68 * Return a node for "self->%error".
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.
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.
78 dt_sugar_new_error_var(void)
80 return (dt_node_op2(DT_TOK_PTR
, dt_node_ident(strdup("self")),
81 dt_node_ident(strdup("%error"))));
85 * Append this clause to the clause list.
88 dt_sugar_append_clause(dt_sugar_parse_t
*dp
, dt_node_t
*clause
)
90 dp
->dtsp_clause_list
= dt_node_link(dp
->dtsp_clause_list
, clause
);
94 * Prepend this clause to the clause list.
97 dt_sugar_prepend_clause(dt_sugar_parse_t
*dp
, dt_node_t
*clause
)
99 dp
->dtsp_clause_list
= dt_node_link(clause
, dp
->dtsp_clause_list
);
103 * Return a node for "this->%condition_<condid>", or NULL if condid==0.
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.
109 dt_sugar_new_condition_var(int condid
)
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
)));
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:
130 * this->%condition_<newcond> =
131 * (this->%condition_<condid> && pred);
134 * Note: if condid==0, we will instead do "... = (1 && pred)", to effectively
135 * convert the pred to a boolean.
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.
142 dt_sugar_new_condition_impl(dt_sugar_parse_t
*dp
,
143 dt_node_t
*pred
, int condid
, int newcond
)
145 dt_node_t
*value
, *body
, *newpred
;
147 /* predicate is !self->%error */
148 newpred
= dt_node_op1(DT_TOK_LNEG
, dt_sugar_new_error_var());
152 * value is (1 && pred)
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.
160 value
= dt_node_op2(DT_TOK_LAND
, dt_node_int(1), pred
);
162 /* value is (this->%condition_<condid> && pred) */
163 value
= dt_node_op2(DT_TOK_LAND
,
164 dt_sugar_new_condition_var(condid
), pred
);
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
));
171 return (dt_node_clause(dp
->dtsp_pdescs
, newpred
, body
));
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.
180 dt_sugar_new_condition(dt_sugar_parse_t
*dp
, dt_node_t
*pred
, int condid
)
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
);
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).
193 dt_sugar_visit_all(dt_sugar_parse_t
*dp
, dt_node_t
*dnp
)
197 switch (dnp
->dn_kind
) {
209 for (arg
= dnp
->dn_args
; arg
!= NULL
; arg
= arg
->dn_list
)
210 dt_sugar_visit_all(dp
, arg
);
214 dt_sugar_visit_all(dp
, dnp
->dn_child
);
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
);
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
);
237 dt_sugar_visit_all(dp
, dnp
->dn_expr
);
241 for (arg
= dnp
->dn_aggtup
; arg
!= NULL
; arg
= arg
->dn_list
)
242 dt_sugar_visit_all(dp
, arg
);
245 dt_sugar_visit_all(dp
, dnp
->dn_aggfun
);
249 for (arg
= dnp
->dn_pdescs
; arg
!= NULL
; arg
= arg
->dn_list
)
250 dt_sugar_visit_all(dp
, arg
);
252 if (dnp
->dn_pred
!= NULL
)
253 dt_sugar_visit_all(dp
, dnp
->dn_pred
);
255 for (arg
= dnp
->dn_acts
; arg
!= NULL
; arg
= arg
->dn_list
)
256 dt_sugar_visit_all(dp
, arg
);
259 case DT_NODE_INLINE
: {
260 const dt_idnode_t
*inp
= dnp
->dn_ident
->di_iarg
;
262 dt_sugar_visit_all(dp
, inp
->din_root
);
266 if (dnp
->dn_membexpr
)
267 dt_sugar_visit_all(dp
, dnp
->dn_membexpr
);
271 for (arg
= dnp
->dn_members
; arg
!= NULL
; arg
= arg
->dn_list
)
272 dt_sugar_visit_all(dp
, arg
);
275 case DT_NODE_PROVIDER
:
276 for (arg
= dnp
->dn_probes
; arg
!= NULL
; arg
= arg
->dn_list
)
277 dt_sugar_visit_all(dp
, arg
);
281 for (arg
= dnp
->dn_list
; arg
!= NULL
; arg
= arg
->dn_list
)
282 dt_sugar_visit_all(dp
, arg
);
287 dt_sugar_visit_all(dp
, dnp
->dn_conditional
);
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
;
293 dt_sugar_visit_all(dp
, arg
);
298 (void) dnerror(dnp
, D_UNKNOWN
, "bad node %p, kind %d\n",
299 (void *)dnp
, dnp
->dn_kind
);
304 * Return a new clause which resets the error variable to zero:
306 * dp_pdescs{ self->%error = 0; }
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
313 dt_sugar_new_clearerror_clause(dt_sugar_parse_t
*dp
)
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
));
321 * Evaluate the conditional, and recursively visit the body of the "if"
322 * statement (and the "else", if present).
325 dt_sugar_do_if(dt_sugar_parse_t
*dp
, dt_node_t
*if_stmt
, int precondition
)
329 assert(if_stmt
->dn_kind
== DT_NODE_IF
);
332 newid
= dt_sugar_new_condition(dp
,
333 if_stmt
->dn_conditional
, precondition
);
336 dt_sugar_visit_stmts(dp
, if_stmt
->dn_body
, newid
);
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
343 if (if_stmt
->dn_alternate_body
!= NULL
) {
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
));
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.
356 * /!self->%error && this->%condition_<condid>/
362 dt_sugar_new_basic_block(dt_sugar_parse_t
*dp
, int condid
, dt_node_t
*stmts
)
364 dt_node_t
*pred
= NULL
;
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
372 if (dp
->dtsp_num_conditions
!= 0) {
373 pred
= dt_node_op1(DT_TOK_LNEG
,
374 dt_sugar_new_error_var());
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
));
381 dt_sugar_append_clause(dp
,
382 dt_node_clause(dp
->dtsp_pdescs
, pred
, stmts
));
386 * Visit all the statements in this list, and break them into basic blocks,
387 * generating new clauses for "if" and "else" statements.
390 dt_sugar_visit_stmts(dt_sugar_parse_t
*dp
, dt_node_t
*stmts
, int precondition
)
393 dt_node_t
*prev_stmt
= NULL
;
394 dt_node_t
*next_stmt
;
395 dt_node_t
*first_stmt_in_basic_block
= NULL
;
397 for (stmt
= stmts
; stmt
!= NULL
; stmt
= next_stmt
) {
398 next_stmt
= stmt
->dn_list
;
400 if (stmt
->dn_kind
!= DT_NODE_IF
) {
401 if (first_stmt_in_basic_block
== NULL
)
402 first_stmt_in_basic_block
= stmt
;
408 * Remove this and following statements from the previous
411 if (prev_stmt
!= NULL
)
412 prev_stmt
->dn_list
= NULL
;
415 * Generate clause for statements preceding the "if"
417 if (first_stmt_in_basic_block
!= NULL
) {
418 dt_sugar_new_basic_block(dp
, precondition
,
419 first_stmt_in_basic_block
);
422 dt_sugar_do_if(dp
, stmt
, precondition
);
424 first_stmt_in_basic_block
= NULL
;
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
);
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).
441 * dtrace:::ERROR{ self->%error = 1; }
444 dt_sugar_makeerrorclause(void)
446 dt_node_t
*acts
, *pdesc
;
448 pdesc
= dt_node_pdesc_by_name(strdup("dtrace:::ERROR"));
450 acts
= dt_node_statement(dt_node_op2(DT_TOK_ASGN
,
451 dt_sugar_new_error_var(), dt_node_int(1)));
453 return (dt_node_clause(pdesc
, NULL
, acts
));
457 * Transform the super-clause into straight-D, returning the new list of
461 dt_compile_sugar(dtrace_hdl_t
*dtp
, dt_node_t
*clause
)
463 dt_sugar_parse_t dp
= { 0 };
467 dp
.dtsp_pdescs
= clause
->dn_pdescs
;
469 /* make dt_node_int() generate an "int"-typed integer */
470 yyintdecimal
= B_TRUE
;
471 yyintsuffix
[0] = '\0';
474 dt_sugar_visit_all(&dp
, clause
);
476 if (dp
.dtsp_num_ifs
== 0 && dp
.dtsp_num_conditions
== 0) {
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
484 dt_sugar_append_clause(&dp
, dt_node_clause(clause
->dn_pdescs
,
485 clause
->dn_pred
, clause
->dn_acts
));
487 if (clause
->dn_pred
!= NULL
) {
488 condid
= dt_sugar_new_condition(&dp
,
489 clause
->dn_pred
, condid
);
492 if (clause
->dn_acts
== NULL
) {
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.
499 dt_sugar_new_basic_block(&dp
, condid
, NULL
);
501 dt_sugar_visit_stmts(&dp
, clause
->dn_acts
, condid
);
505 if (dp
.dtsp_num_conditions
!= 0) {
506 dt_sugar_prepend_clause(&dp
,
507 dt_sugar_new_clearerror_clause(&dp
));
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());
515 return (dp
.dtsp_clause_list
);