michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: require({ version: '1.8' }); michael@0: require({ after_gcc_pass: 'cfg' }); michael@0: michael@0: include('treehydra.js'); michael@0: michael@0: include('util.js'); michael@0: include('gcc_util.js'); michael@0: include('gcc_print.js'); michael@0: include('unstable/adts.js'); michael@0: include('unstable/analysis.js'); michael@0: include('unstable/esp.js'); michael@0: michael@0: /* This implements the control flows-through analysis in bug 432917 */ michael@0: var Zero_NonZero = {} michael@0: include('unstable/zero_nonzero.js', Zero_NonZero); michael@0: michael@0: MapFactory.use_injective = true; michael@0: michael@0: // Print a trace for each function analyzed michael@0: let TRACE_FUNCTIONS = 0; michael@0: // Trace operation of the ESP analysis, use 2 or 3 for more detail michael@0: let TRACE_ESP = 0; michael@0: // Print time-taken stats michael@0: let TRACE_PERF = 0; michael@0: michael@0: function process_tree(fndecl) { michael@0: // At this point we have a function we want to analyze michael@0: if (TRACE_FUNCTIONS) { michael@0: print('* function ' + decl_name(fndecl)); michael@0: print(' ' + loc_string(location_of(fndecl))); michael@0: } michael@0: if (TRACE_PERF) timer_start(fstring); michael@0: michael@0: let cfg = function_decl_cfg(fndecl); michael@0: michael@0: try { michael@0: let trace = TRACE_ESP; michael@0: let a = new FlowCheck(cfg, trace); michael@0: a.run(); michael@0: } catch (e if e == "skip") { michael@0: return michael@0: } michael@0: print("checked " + decl_name(fndecl)) michael@0: if (cfg.x_exit_block_ptr.stateIn.substates) michael@0: for each (let substate in cfg.x_exit_block_ptr.stateIn.substates.getValues()) { michael@0: for each (let v in substate.getVariables()) { michael@0: let var_state= substate.get (v) michael@0: let blame = substate.getBlame(v) michael@0: if (var_state != ESP.TOP && typeof var_state == 'string') michael@0: error(decl_name(fndecl) + ": Control did not flow through " +var_state, location_of(blame)) michael@0: } michael@0: } michael@0: michael@0: if (TRACE_PERF) timer_stop(fstring); michael@0: } michael@0: michael@0: let track_return_loc = 0; michael@0: const FLOW_THROUGH = "MUST_FLOW_THROUGH" michael@0: michael@0: function FlowCheck(cfg, trace) { michael@0: let found = create_decl_set(); // ones we already found michael@0: for (let bb in cfg_bb_iterator(cfg)) { michael@0: for (let isn in bb_isn_iterator(bb)) { michael@0: switch (isn.tree_code()) { michael@0: case GIMPLE_CALL: { michael@0: let fn = gimple_call_fndecl(isn) michael@0: if (!fn || decl_name(fn) != FLOW_THROUGH) michael@0: continue; michael@0: this.must_flow_fn = fn michael@0: break michael@0: } michael@0: case GIMPLE_RETURN: michael@0: let ret_expr = return_expr(isn); michael@0: if (track_return_loc && ret_expr) { michael@0: TREE_CHECK(ret_expr, VAR_DECL, RESULT_DECL); michael@0: this.rval = ret_expr; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: if (!this.must_flow_fn) michael@0: throw "skip" michael@0: michael@0: let psvar_list = [new ESP.PropVarSpec(this.must_flow_fn, true)] michael@0: michael@0: if (this.rval) michael@0: psvar_list.push(new ESP.PropVarSpec(this.rval)) michael@0: michael@0: this.zeroNonzero = new Zero_NonZero.Zero_NonZero() michael@0: ESP.Analysis.call(this, cfg, psvar_list, Zero_NonZero.meet, trace); michael@0: } michael@0: michael@0: FlowCheck.prototype = new ESP.Analysis; michael@0: michael@0: function char_star_arg2string(tree) { michael@0: return TREE_STRING_POINTER(tree.tree_check(ADDR_EXPR).operands()[0].tree_check(ARRAY_REF).operands()[0]) michael@0: } michael@0: michael@0: // State transition function. Mostly, we delegate everything to michael@0: // another function as either an assignment or a call. michael@0: FlowCheck.prototype.flowState = function(isn, state) { michael@0: switch (TREE_CODE(isn)) { michael@0: case GIMPLE_CALL: { michael@0: let fn = gimple_call_fndecl(isn) michael@0: if (fn == this.must_flow_fn) michael@0: state.assignValue(fn, char_star_arg2string(gimple_call_arg(isn, 0)), isn) michael@0: break michael@0: } michael@0: case GIMPLE_LABEL: { michael@0: let label = decl_name(gimple_op(isn, 0)) michael@0: for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) { michael@0: if (label != value) continue michael@0: // reached the goto label we wanted =D michael@0: state.assignValue(this.must_flow_fn, ESP.TOP, isn) michael@0: } michael@0: break michael@0: } michael@0: case GIMPLE_RETURN: michael@0: for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) { michael@0: if (typeof value != 'string') continue michael@0: let loc; michael@0: if (this.rval) michael@0: for ([value, blame] in state.yieldPreconditions(this.rval)) { michael@0: loc = value michael@0: break michael@0: } michael@0: error("return without going through label "+ value, loc); michael@0: return michael@0: } michael@0: break michael@0: case GIMPLE_ASSIGN: michael@0: if (track_return_loc && gimple_op(isn, 0) == this.rval) { michael@0: state.assignValue(this.rval, location_of(isn), isn) michael@0: break michael@0: } michael@0: default: michael@0: this.zeroNonzero.flowState(isn, state) michael@0: } michael@0: } michael@0: michael@0: // State transition function to apply branch filters. This is kind michael@0: // of boilerplate--we're just handling some stuff that GCC generates. michael@0: FlowCheck.prototype.flowStateCond = function(isn, truth, state) { michael@0: this.zeroNonzero.flowStateCond (isn, truth, state) michael@0: }