|
| 1 | +// Copyright 2018 King's College London. |
| 2 | +// Created by the Software Development Team <http://soft-dev.org/>. |
| 3 | +// |
| 4 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 5 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 6 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 7 | +// option. This file may not be copied, modified, or distributed |
| 8 | +// except according to those terms. |
| 9 | + |
| 10 | +use rustc::ty::{self, TyCtxt, List}; |
| 11 | +use rustc::mir::{Operand, LocalDecl, Place, SourceInfo, BasicBlock, Local, BasicBlockData, |
| 12 | + TerminatorKind, Terminator, OUTERMOST_SOURCE_SCOPE, Constant, Mir}; |
| 13 | +use rustc_data_structures::indexed_vec::Idx; |
| 14 | +use syntax_pos::DUMMY_SP; |
| 15 | +use syntax::attr; |
| 16 | +use transform::{MirPass, MirSource}; |
| 17 | +use rustc::hir; |
| 18 | +use rustc::hir::def_id::{DefIndex, LOCAL_CRATE}; |
| 19 | +use rustc::hir::map::blocks::FnLikeNode; |
| 20 | + |
| 21 | +/// A MIR pass which, for each basic block, inserts a call to the software trace recorder. |
| 22 | +/// The call arguments passed uniquely identify the MIR location. |
| 23 | +pub struct AddYkSWTCalls(pub DefIndex); |
| 24 | + |
| 25 | +impl MirPass for AddYkSWTCalls { |
| 26 | + fn run_pass<'a, 'tcx>(&self, |
| 27 | + tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 28 | + src: MirSource, |
| 29 | + mir: &mut Mir<'tcx>) { |
| 30 | + if !should_annotate(tcx, src) { |
| 31 | + return; |
| 32 | + } |
| 33 | + |
| 34 | + // Find the recorder function to call. |
| 35 | + let rec_fn_defid = tcx.get_lang_items(LOCAL_CRATE).yk_swt_rec_loc_wrap() |
| 36 | + .expect("couldn't find software trace recorder function"); |
| 37 | + |
| 38 | + // Types. |
| 39 | + let unit_ty = tcx.mk_unit(); |
| 40 | + let u32_ty = tcx.types.u32; |
| 41 | + let u64_ty = tcx.types.u64; |
| 42 | + |
| 43 | + // Each block is replaced by a new block whose terminator calls the recorder function. |
| 44 | + let mut replace_blocks = Vec::new(); |
| 45 | + |
| 46 | + // The original blocks are copied and the recorder function returns to a copy. |
| 47 | + let mut copied_blocks = Vec::new(); |
| 48 | + |
| 49 | + // New local decls are required to accomodate the (unit) return value of the recorder func. |
| 50 | + let mut new_local_decls = Vec::new(); |
| 51 | + |
| 52 | + let num_orig_blocks = mir.basic_blocks().len(); |
| 53 | + let num_orig_local_decls = mir.local_decls.len(); |
| 54 | + let local_crate_hash = tcx.crate_hash(LOCAL_CRATE).as_u64(); |
| 55 | + |
| 56 | + for (bb, bb_data) in mir.basic_blocks_mut().iter_enumerated() { |
| 57 | + // Copy the original block and compute what its index will be once we have pushed onto |
| 58 | + // the end of the MIR's basic block vector. |
| 59 | + let new_blk = bb_data.clone(); |
| 60 | + let new_blk_idx = BasicBlock::new(num_orig_blocks + copied_blocks.len()); |
| 61 | + copied_blocks.push(new_blk); |
| 62 | + |
| 63 | + // Prepare to call the recorder function. |
| 64 | + let ret_val = LocalDecl::new_temp(unit_ty, DUMMY_SP); |
| 65 | + let ret_place = Place::Local(Local::new(num_orig_local_decls + new_local_decls.len())); |
| 66 | + new_local_decls.push(ret_val); |
| 67 | + |
| 68 | + let crate_hash_oper = Operand::Constant(box Constant { |
| 69 | + span: DUMMY_SP, |
| 70 | + ty: u64_ty, |
| 71 | + user_ty: None, |
| 72 | + literal: ty::Const::from_u64(tcx, local_crate_hash), |
| 73 | + }); |
| 74 | + |
| 75 | + let def_idx_oper = Operand::Constant(box Constant { |
| 76 | + span: DUMMY_SP, |
| 77 | + ty: u32_ty, |
| 78 | + user_ty: None, |
| 79 | + literal: ty::Const::from_u32(tcx, self.0.as_raw_u32()), |
| 80 | + }); |
| 81 | + |
| 82 | + let bb_oper = Operand::Constant(box Constant { |
| 83 | + span: DUMMY_SP, |
| 84 | + ty: u32_ty, |
| 85 | + user_ty: None, |
| 86 | + literal: ty::Const::from_u32(tcx, bb.index() as u32), |
| 87 | + }); |
| 88 | + |
| 89 | + let rec_fn_oper = Operand::function_handle(tcx, rec_fn_defid, |
| 90 | + List::empty(), DUMMY_SP); |
| 91 | + |
| 92 | + let term_kind = TerminatorKind::Call { |
| 93 | + func: rec_fn_oper, |
| 94 | + args: vec![crate_hash_oper, def_idx_oper, bb_oper], |
| 95 | + destination: Some((ret_place, new_blk_idx)), |
| 96 | + cleanup: None, |
| 97 | + from_hir_call: false, |
| 98 | + }; |
| 99 | + |
| 100 | + // Construct a new block to replace the original one. |
| 101 | + let source_info = bb_data.terminator.clone().map(|t| t.source_info) |
| 102 | + .or(Some(SourceInfo { span: DUMMY_SP, scope: OUTERMOST_SOURCE_SCOPE })).unwrap(); |
| 103 | + let replace_block = BasicBlockData { |
| 104 | + statements: vec![], |
| 105 | + terminator: Some(Terminator { |
| 106 | + source_info, |
| 107 | + kind: term_kind |
| 108 | + }), |
| 109 | + is_cleanup: false |
| 110 | + }; |
| 111 | + replace_blocks.push(replace_block); |
| 112 | + } |
| 113 | + |
| 114 | + // Finally, commit our transformations. |
| 115 | + mir.basic_blocks_mut().extend(copied_blocks); |
| 116 | + mir.local_decls.extend(new_local_decls); |
| 117 | + for (bb, bb_data) in replace_blocks.drain(..).enumerate() { |
| 118 | + mir.basic_blocks_mut()[BasicBlock::new(bb)] = bb_data; |
| 119 | + } |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +/// Given a `MirSource`, decides if we should annotate the correpsonding MIR. |
| 124 | +fn should_annotate(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) -> bool { |
| 125 | + // Never annotate any MIR-like thing marked `#[no_trace]` or `#[naked]`. The trace record and |
| 126 | + // wrapper are also marked `#[no_trace]` to prevent infinite recursion. |
| 127 | + for attr in tcx.get_attrs(src.def_id).iter() { |
| 128 | + if attr.check_name("no_trace") { |
| 129 | + return false; |
| 130 | + } |
| 131 | + if attr.check_name("naked") { |
| 132 | + return false; |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + // If there is a crate level `#![no_trace]` attribute, honour that. |
| 137 | + for attr in tcx.hir.krate_attrs() { |
| 138 | + if attr.check_name("no_trace") { |
| 139 | + return false; |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + // We can't call the software tracing function if there is no libcore. |
| 144 | + if attr::contains_name(tcx.hir.krate_attrs(), "no_core") { |
| 145 | + return false; |
| 146 | + } |
| 147 | + |
| 148 | + // The libcompiler_builtins crate is special and we can't annotate it. |
| 149 | + if tcx.is_compiler_builtins(LOCAL_CRATE) { |
| 150 | + return false; |
| 151 | + } |
| 152 | + |
| 153 | + // We can't add calls to promoted items. |
| 154 | + if let Some(_) = src.promoted { |
| 155 | + return false; |
| 156 | + } |
| 157 | + |
| 158 | + // We can't add calls to constant functions. |
| 159 | + let node_id = tcx.hir.as_local_node_id(src.def_id) |
| 160 | + .expect("Failed to get node id"); |
| 161 | + if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) { |
| 162 | + if fn_like.constness() == hir::Constness::Const { |
| 163 | + return false; |
| 164 | + } |
| 165 | + } else { |
| 166 | + return false; |
| 167 | + } |
| 168 | + |
| 169 | + true |
| 170 | +} |
0 commit comments