1 require({ version
: '1.8' });
2 require({ after_gcc_pass
: 'cfg' });
4 include('treehydra.js');
7 include('gcc_util.js');
8 include('gcc_print.js');
9 include('unstable/adts.js');
10 include('unstable/analysis.js');
11 include('unstable/esp.js');
13 /* This implements the control flows-through analysis in bug 432917 */
15 include('unstable/zero_nonzero.js', Zero_NonZero
);
17 MapFactory
.use_injective
= true;
19 // Print a trace for each function analyzed
20 let TRACE_FUNCTIONS
= 0;
21 // Trace operation of the ESP analysis, use 2 or 3 for more detail
23 // Print time-taken stats
26 function process_tree(fndecl
) {
27 // At this point we have a function we want to analyze
28 if (TRACE_FUNCTIONS
) {
29 print('* function ' + decl_name(fndecl
));
30 print(' ' + loc_string(location_of(fndecl
)));
32 if (TRACE_PERF
) timer_start(fstring
);
34 let cfg
= function_decl_cfg(fndecl
);
38 let trace
= TRACE_ESP
;
39 let a
= new FlowCheck(cfg
, trace
);
41 } catch (e
if e
== "skip") {
44 print("checked " + decl_name(fndecl
))
45 if (cfg
.x_exit_block_ptr
.stateIn
.substates
)
46 for each (let substate
in cfg
.x_exit_block_ptr
.stateIn
.substates
.getValues()) {
47 for each (let v
in substate
.getVariables()) {
48 let var_state
= substate
.get (v
)
49 let blame
= substate
.getBlame(v
)
50 if (var_state
!= ESP
.TOP
&& typeof var_state
== 'string')
51 error(decl_name(fndecl
) + ": Control did not flow through " +var_state
, location_of(blame
))
55 if (TRACE_PERF
) timer_stop(fstring
);
58 let track_return_loc
= 0;
59 const FLOW_THROUGH
= "MUST_FLOW_THROUGH"
61 function FlowCheck(cfg
, trace
) {
62 let found
= create_decl_set(); // ones we already found
63 for (let bb
in cfg_bb_iterator(cfg
)) {
64 for (let isn
in bb_isn_iterator(bb
)) {
65 switch (isn
.tree_code()) {
67 let fn
= call_function_decl(isn
)
68 if (!fn
|| decl_name(fn
) != FLOW_THROUGH
)
70 this.must_flow_fn
= fn
74 let ret_expr
= isn
.operands()[0]
75 if (track_return_loc
&& ret_expr
)
76 switch (ret_expr
.tree_code()) {
77 case GIMPLE_MODIFY_STMT
:
78 this.rval
= ret_expr
.operands()[1].tree_check(VAR_DECL
)
84 throw new Error("Unhandled return expression")
90 if (!this.must_flow_fn
)
93 let psvar_list
= [new ESP
.PropVarSpec(this.must_flow_fn
, true)]
96 psvar_list
.push(new ESP
.PropVarSpec(this.rval
))
98 this.zeroNonzero
= new Zero_NonZero
.Zero_NonZero()
99 ESP
.Analysis
.call(this, cfg
, psvar_list
, Zero_NonZero
.meet
, trace
);
102 FlowCheck
.prototype = new ESP
.Analysis
;
104 function char_star_arg2string(tree
) {
105 return TREE_STRING_POINTER(tree
.tree_check(ADDR_EXPR
).operands()[0].tree_check(ARRAY_REF
).operands()[0])
108 // State transition function. Mostly, we delegate everything to
109 // another function as either an assignment or a call.
110 FlowCheck
.prototype.flowState = function(isn
, state
) {
111 switch (TREE_CODE(isn
)) {
113 let fn
= call_function_decl(isn
)
114 if (fn
== this.must_flow_fn
)
115 state
.assignValue(fn
, char_star_arg2string(call_arg(isn
, 0)), isn
)
119 let label
= decl_name(isn
.operands()[0])
120 for ([value
, blame
] in state
.yieldPreconditions(this.must_flow_fn
)) {
121 if (label
!= value
) continue
122 // reached the goto label we wanted =D
123 state
.assignValue(this.must_flow_fn
, ESP
.TOP
, isn
)
128 for ([value
, blame
] in state
.yieldPreconditions(this.must_flow_fn
)) {
129 if (typeof value
!= 'string') continue
132 for ([value
, blame
] in state
.yieldPreconditions(this.rval
)) {
136 error("return without going through label "+ value
, loc
);
141 case GIMPLE_MODIFY_STMT
:
142 if (track_return_loc
&& isn
.operands()[0] == this.rval
) {
143 state
.assignValue(this.rval
, location_of(isn
), isn
)
147 this.zeroNonzero
.flowState(isn
, state
)
151 // State transition function to apply branch filters. This is kind
152 // of boilerplate--we're just handling some stuff that GCC generates.
153 FlowCheck
.prototype.flowStateCond = function(isn
, truth
, state
) {
154 this.zeroNonzero
.flowStateCond (isn
, truth
, state
)