3 * Routines for parsing basic blocks from the IL stream
6 * Rodrigo Kumpera (rkumpera@novell.com)
8 * Copyright 2010 Novell, Inc (http://www.novell.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #include <mono/metadata/debug-helpers.h>
15 #include <mono/metadata/metadata-internals.h>
16 #include <mono/metadata/mono-endian.h>
17 #include <mono/metadata/mono-basic-block.h>
18 #include <mono/metadata/opcodes.h>
20 #include <mono/utils/mono-error-internals.h>
21 #include <mono/utils/mono-compiler.h>
23 #define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a))
24 #define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a))
26 #if SIZEOF_VOID_P == 4
27 #define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b)
29 #define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b)
32 #define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b))
33 #define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b))
45 dump_bb_list (MonoSimpleBasicBlock
*bb
, MonoSimpleBasicBlock
**root
, const char *msg
)
47 printf ("------- %s --- root is %x ---\n", msg
, (*root
)->start
);
50 printf ("BB start %x end %x left ", bb
->start
, bb
->end
);
52 printf ("%x", bb
->left
->start
);
58 printf ("%x", bb
->right
->start
);
64 printf ("%x", bb
->parent
->start
);
68 printf(" color %s out [", bb
->colour
== RED
? "red" : "black");
70 for (tmp
= bb
->out_bb
; tmp
; tmp
= tmp
->next
) {
71 MonoSimpleBasicBlock
*to
= tmp
->data
;
72 printf ("%x ", to
->start
);
74 printf ("] %s\n", bb
->dead
? "dead" : "alive");
82 bb_unlink (MonoSimpleBasicBlock
*from
, MonoSimpleBasicBlock
*to
)
85 from
->out_bb
= g_slist_remove (from
->out_bb
, to
);
89 bb_link (MonoSimpleBasicBlock
*from
, MonoSimpleBasicBlock
*to
)
91 if (g_slist_find (from
->out_bb
, to
))
93 from
->out_bb
= g_slist_prepend (from
->out_bb
, to
);
97 static MonoSimpleBasicBlock
*
98 bb_grandparent (MonoSimpleBasicBlock
*bb
)
100 return bb
&& bb
->parent
? bb
->parent
->parent
: NULL
;
103 static MonoSimpleBasicBlock
*
104 bb_uncle (MonoSimpleBasicBlock
*bb
)
106 MonoSimpleBasicBlock
*gp
= bb_grandparent (bb
);
109 if (bb
->parent
== gp
->left
)
115 change_node (MonoSimpleBasicBlock
*from
, MonoSimpleBasicBlock
*to
, MonoSimpleBasicBlock
**root
)
117 MonoSimpleBasicBlock
*parent
= from
->parent
;
119 if (parent
->left
== from
)
130 rotate_right (MonoSimpleBasicBlock
*parent
, MonoSimpleBasicBlock
**root
)
132 MonoSimpleBasicBlock
*bb
= parent
->left
;
134 parent
->left
= bb
->right
;
135 parent
->left
->parent
= parent
;
139 change_node (parent
, bb
, root
);
144 rotate_left (MonoSimpleBasicBlock
*bb
, MonoSimpleBasicBlock
**root
)
146 MonoSimpleBasicBlock
*other
= bb
->right
;
148 bb
->right
= other
->left
;
149 bb
->right
->parent
= bb
;
153 change_node (bb
, other
, root
);
157 /* School book implementation of a RB tree with insert then fix (which requires a parent pointer)
158 * TODO implement Sedgewick's version that doesn't require parent pointers
161 bb_insert (MonoSimpleBasicBlock
*first
, MonoSimpleBasicBlock
*bb
, MonoSimpleBasicBlock
**root
)
163 MonoSimpleBasicBlock
*parent
, *uncle
, *grandparent
;
164 int bb_start
= bb
->start
;
168 if (bb_start
< parent
->start
) {
169 if (parent
->left
== NULL
) {
173 parent
= parent
->left
;
175 if (parent
->right
== NULL
) {
179 parent
= parent
->right
;
188 if (bb
->parent
== NULL
) {
193 if (bb
->parent
->colour
== BLACK
)
196 uncle
= bb_uncle (bb
);
197 if (uncle
&& uncle
->colour
== RED
) {
198 grandparent
= bb_grandparent (bb
);
200 bb
->parent
->colour
= BLACK
;
201 uncle
->colour
= BLACK
;
202 grandparent
->colour
= RED
;
207 grandparent
= bb_grandparent (bb
);
208 if ((bb
== bb
->parent
->right
) && (bb
->parent
== grandparent
->left
)) {
209 rotate_left (bb
->parent
, root
);
211 } else if ((bb
== bb
->parent
->left
) && (bb
->parent
== grandparent
->right
)) {
212 rotate_right (bb
->parent
, root
);
216 grandparent
= bb_grandparent (bb
);
217 bb
->parent
->colour
= BLACK
;
218 grandparent
->colour
= RED
;
219 if ((bb
== bb
->parent
->left
) && (bb
->parent
== grandparent
->left
))
220 rotate_right (grandparent
, root
);
222 rotate_left (grandparent
, root
);
228 bb_idx_is_contained (MonoSimpleBasicBlock
*bb
, int target
)
230 return bb
->start
<= target
&& target
< bb
->end
;
234 * Split the basic blocks from @first at @target.
235 * @hint is a guess of a very close to the target basic block. It is probed before the RB tree as it's often possible
236 * to provide a near to exact guess (next block splits, switch branch targets, etc)
239 static MonoSimpleBasicBlock
*
240 bb_split (MonoSimpleBasicBlock
*first
, MonoSimpleBasicBlock
*hint
, MonoSimpleBasicBlock
**root
, guint target
, gboolean link_blocks
, MonoMethod
*method
, MonoError
*error
)
242 MonoSimpleBasicBlock
*res
, *bb
= first
;
246 if (bb_idx_is_contained (hint
, target
)) {
248 } else if (hint
->next
&& bb_idx_is_contained (hint
->next
, target
)) {
253 if (bb_idx_is_contained (first
, target
))
255 if (first
->start
> target
)
258 first
= first
->right
;
263 mono_error_set_not_verifiable (error
, method
, "Invalid instruction target %x", target
);
267 if (first
->start
== target
)
270 res
= g_new0 (MonoSimpleBasicBlock
, 1);
272 res
->end
= first
->end
;
273 res
->next
= first
->next
;
274 res
->out_bb
= first
->out_bb
;
277 first
->end
= res
->start
;
279 first
->out_bb
= NULL
;
282 bb_link (first
, res
);
283 bb_insert (bb
, res
, root
);
289 bb_liveness (MonoSimpleBasicBlock
*bb
)
291 GPtrArray
* mark_stack
= g_ptr_array_new ();
294 /*All function entry points (prologue, EH handler/filter) are already marked*/
297 g_ptr_array_add (mark_stack
, bb
);
301 while (mark_stack
->len
> 0) {
302 MonoSimpleBasicBlock
*block
= (MonoSimpleBasicBlock
*)g_ptr_array_remove_index_fast (mark_stack
, mark_stack
->len
- 1);
305 for (tmp
= block
->out_bb
; tmp
; tmp
= tmp
->next
) {
306 MonoSimpleBasicBlock
*to
= (MonoSimpleBasicBlock
*)tmp
->data
;
308 g_ptr_array_add (mark_stack
, to
);
312 g_ptr_array_free (mark_stack
, TRUE
);
315 /*This doesn't returns endfilter because it's not useful to split at its boundary.
316 Endfilter must be the last instruction of a filter clause and MS enforces this, so we can't have
320 mono_opcode_has_static_branch (int opcode
)
325 case MONO_CEE_RETHROW
:
326 case MONO_CEE_ENDFINALLY
:
327 case MONO_CEE_MONO_RETHROW
:
335 bb_formation_il_pass (const unsigned char *start
, const unsigned char *end
, MonoSimpleBasicBlock
*bb
, MonoSimpleBasicBlock
**root
, MonoMethod
*method
, MonoError
*error
)
337 unsigned const char *ip
= start
;
338 MonoOpcodeEnum value
;
340 guint cli_addr
, offset
;
341 MonoSimpleBasicBlock
*branch
, *next
, *current
;
342 const MonoOpcode
*opcode
;
349 cli_addr
= ip
- start
;
350 size
= mono_opcode_value_and_size (&ip
, end
, &value
);
352 mono_error_set_not_verifiable (error
, method
, "Invalid instruction %x", *ip
);
356 while (current
&& cli_addr
>= current
->end
)
357 current
= current
->next
;
360 opcode
= &mono_opcodes
[value
];
361 switch (opcode
->argument
) {
364 if (!mono_opcode_has_static_branch (value
) || ip
>= end
)
366 if (!(next
= bb_split (bb
, current
, root
, ip
- start
, FALSE
, method
, error
)))
369 bb_unlink (current
, next
);
372 case MonoInlineString
:
374 case MonoInlineField
:
377 case MonoShortInlineR
:
382 case MonoInlineMethod
:
384 if (value
!= MONO_CEE_JMP
|| ip
>= end
)
386 if (!(next
= bb_split (bb
, current
, root
, ip
- start
, FALSE
, method
, error
)))
389 bb_unlink (current
, next
);
396 case MonoShortInlineVar
:
397 case MonoShortInlineI
:
404 case MonoShortInlineBrTarget
:
405 case MonoInlineBrTarget
:
406 if (opcode
->argument
== MonoShortInlineBrTarget
) {
407 offset
= cli_addr
+ 2 + (signed char)ip
[1];
410 offset
= cli_addr
+ 5 + (gint32
)read32 (ip
+ 1);
414 branch
= bb_split (bb
, current
, root
, offset
, TRUE
, method
, error
);
418 /*If we splitted the current BB*/
419 if (offset
< cli_addr
&& branch
->start
> current
->start
)
422 next
= bb_split (bb
, current
, root
, ip
- start
, opcode
->flow_type
!= MONO_FLOW_BRANCH
, method
, error
);
429 bb_link (current
, branch
);
430 if (next
&& opcode
->flow_type
== MONO_FLOW_BRANCH
&& next
!= branch
) {
431 bb_unlink (current
, next
);
435 case MonoInlineSwitch
: {
436 MonoSimpleBasicBlock
*tmp
;
437 guint32 j
, n
= read32 (ip
+ 1);
440 offset
= cli_addr
+ 5 + 4 * n
;
441 if (!(next
= bb_split (bb
, current
, root
, offset
, TRUE
, method
, error
)))
444 bb_link (current
, next
);
447 for (j
= 0; j
< n
; ++j
) {
449 mono_error_set_not_verifiable (error
, method
, "Invalid switch instruction %x", cli_addr
);
452 if (!(next
= bb_split (bb
, next
, root
, offset
+ (gint32
)read32 (ip
), TRUE
, method
, error
)))
454 bb_link (current
, next
);
461 mono_error_set_not_verifiable (error
, method
, "Invalid instruction %x", *ip
);
466 mono_error_set_not_verifiable (error
, method
, "Invalid last instruction");
470 bb_formation_eh_pass (MonoMethodHeader
*header
, MonoSimpleBasicBlock
*bb
, MonoSimpleBasicBlock
**root
, MonoMethod
*method
, MonoError
*error
)
473 int end
= header
->code_size
;
477 /*We must split at all points to verify for targets in the middle of an instruction*/
478 for (i
= 0; i
< header
->num_clauses
; ++i
) {
479 MonoExceptionClause
*clause
= header
->clauses
+ i
;
480 MonoSimpleBasicBlock
*try_block
, *handler
;
482 if (!(try_block
= bb_split (bb
, bb
, root
, clause
->try_offset
, TRUE
, method
, error
)))
485 handler
= bb_split (bb
, try_block
, root
, clause
->handler_offset
, FALSE
, method
, error
);
488 handler
->dead
= FALSE
;
490 if (clause
->flags
== MONO_EXCEPTION_CLAUSE_FILTER
) {
491 MonoSimpleBasicBlock
*filter
= bb_split (bb
, try_block
, root
, clause
->data
.filter_offset
, FALSE
, method
, error
);
494 filter
->dead
= FALSE
;
497 if (clause
->try_offset
+ clause
->try_len
< end
&& !bb_split (bb
, try_block
, root
, clause
->try_offset
+ clause
->try_len
, FALSE
, method
, error
))
500 if (clause
->handler_offset
+ clause
->handler_len
< end
&& !bb_split (bb
, handler
, root
, clause
->handler_offset
+ clause
->handler_len
, FALSE
, method
, error
))
506 * mono_basic_block_free:
508 * Release the memory associated with the list of basis blocks @bb.
511 mono_basic_block_free (MonoSimpleBasicBlock
*bb
)
514 MonoSimpleBasicBlock
*next
= bb
->next
;
516 g_slist_free (bb
->out_bb
);
523 * mono_basic_block_split:
525 * Return the list of basic blocks of method. Return NULL on failure and set @error.
527 MonoSimpleBasicBlock
*
528 mono_basic_block_split (MonoMethod
*method
, MonoError
*error
, MonoMethodHeader
*header
)
530 MonoSimpleBasicBlock
*bb
, *root
;
531 const unsigned char *start
, *end
;
535 start
= header
->code
;
536 end
= start
+ header
->code_size
;
538 bb
= g_new0 (MonoSimpleBasicBlock
, 1);
540 bb
->end
= end
- start
;
545 bb_formation_il_pass (start
, end
, bb
, &root
, method
, error
);
546 if (!mono_error_ok (error
))
549 bb_formation_eh_pass (header
, bb
, &root
, method
, error
);
550 if (!mono_error_ok (error
))
556 dump_bb_list (bb
, &root
, g_strdup_printf("AFTER LIVENESS %s", mono_method_full_name (method
, TRUE
)));
562 mono_basic_block_free (bb
);
567 * mono_opcode_value_and_size:
569 * Returns the size of the opcode starting at *@ip, or -1 on error.
570 * Value is the opcode number.
573 mono_opcode_value_and_size (const unsigned char **ip
, const unsigned char *end
, MonoOpcodeEnum
*value
)
575 const unsigned char *start
= *ip
, *p
;
576 int i
= *value
= mono_opcode_value (ip
, end
);
578 if (i
< 0 || i
>= MONO_CEE_LAST
)
582 switch (mono_opcodes
[i
].argument
) {
586 case MonoInlineString
:
588 case MonoInlineField
:
589 case MonoInlineMethod
:
592 case MonoShortInlineR
:
594 case MonoInlineBrTarget
:
600 case MonoShortInlineVar
:
601 case MonoShortInlineI
:
602 case MonoShortInlineBrTarget
:
609 case MonoInlineSwitch
: {
611 if (ADDP_IS_GREATER_OR_OVF (p
, 5, end
))
613 entries
= read32 (p
+ 1);
614 if (entries
>= (0xFFFFFFFFU
/ 4))
616 size
= 5 + 4 * entries
;
620 g_error ("Invalid opcode %d argument %d max opcode %d\n", i
, mono_opcodes
[i
].argument
, MONO_CEE_LAST
);
623 if (ADDP_IS_GREATER_OR_OVF (p
, size
, end
))
626 return (p
- start
) + size
;
632 * Returns the size of the opcode starting at @ip, or -1 on error.
635 mono_opcode_size (const unsigned char *ip
, const unsigned char *end
)
638 return mono_opcode_value_and_size (&ip
, end
, &tmp
);