(index<- )        ./libsyntax/ext/asm.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
   2  // file at the top-level directory of this distribution and at
   3  // http://rust-lang.org/COPYRIGHT.
   4  //
   5  // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
   6  // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
   7  // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
   8  // option. This file may not be copied, modified, or distributed
   9  // except according to those terms.
  10  
  11  /*
  12   * Inline assembly support.
  13   */
  14  
  15  use ast;
  16  use codemap::Span;
  17  use ext::base;
  18  use ext::base::*;
  19  use parse;
  20  use parse::token::InternedString;
  21  use parse::token;
  22  
  23  
  24  enum State {
  25      Asm,
  26      Outputs,
  27      Inputs,
  28      Clobbers,
  29      Options,
  30      StateNone
  31  }
  32  
  33  impl State {
  34      fn next(&self) -> State {
  35          match *self {
  36              Asm       => Outputs,
  37              Outputs   => Inputs,
  38              Inputs    => Clobbers,
  39              Clobbers  => Options,
  40              Options   => StateNone,
  41              StateNone => StateNone
  42          }
  43      }
  44  }
  45  
  46  static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
  47  
  48  pub fn expand_asm(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
  49                    -> Box<base::MacResult> {
  50      let mut p = parse::new_parser_from_tts(cx.parse_sess(),
  51                                             cx.cfg(),
  52                                             tts.iter()
  53                                                .map(|x| (*x).clone())
  54                                                .collect());
  55  
  56      let mut asm = InternedString::new("");
  57      let mut asm_str_style = None;
  58      let mut outputs = Vec::new();
  59      let mut inputs = Vec::new();
  60      let mut cons = "".to_owned();
  61      let mut volatile = false;
  62      let mut alignstack = false;
  63      let mut dialect = ast::AsmAtt;
  64  
  65      let mut state = Asm;
  66  
  67      let mut read_write_operands = Vec::new();
  68  
  69      'statement: loop {
  70          match state {
  71              Asm => {
  72                  let (s, style) = match expr_to_str(cx, p.parse_expr(),
  73                                                     "inline assembly must be a string literal.") {
  74                      Some((s, st)) => (s, st),
  75                      // let compilation continue
  76                      None => return DummyResult::expr(sp),
  77                  };
  78                  asm = s;
  79                  asm_str_style = Some(style);
  80              }
  81              Outputs => {
  82                  while p.token != token::EOF &&
  83                        p.token != token::COLON &&
  84                        p.token != token::MOD_SEP {
  85  
  86                      if outputs.len() != 0 {
  87                          p.eat(&token::COMMA);
  88                      }
  89  
  90                      let (constraint, _str_style) = p.parse_str();
  91  
  92                      let span = p.last_span;
  93  
  94                      p.expect(&token::LPAREN);
  95                      let out = p.parse_expr();
  96                      p.expect(&token::RPAREN);
  97  
  98                      // Expands a read+write operand into two operands.
  99                      //
 100                      // Use '+' modifier when you want the same expression
 101                      // to be both an input and an output at the same time.
 102                      // It's the opposite of '=&' which means that the memory
 103                      // cannot be shared with any other operand (usually when
 104                      // a register is clobbered early.)
 105                      let output = match constraint.get().slice_shift_char() {
 106                          (Some('='), _) => None,
 107                          (Some('+'), operand) => {
 108                              // Save a reference to the output
 109                              read_write_operands.push((outputs.len(), out));
 110                              Some(token::intern_and_get_ident("=" + operand))
 111                          }
 112                          _ => {
 113                              cx.span_err(span, "output operand constraint lacks '=' or '+'");
 114                              None
 115                          }
 116                      };
 117  
 118                      outputs.push((output.unwrap_or(constraint), out));
 119                  }
 120              }
 121              Inputs => {
 122                  while p.token != token::EOF &&
 123                        p.token != token::COLON &&
 124                        p.token != token::MOD_SEP {
 125  
 126                      if inputs.len() != 0 {
 127                          p.eat(&token::COMMA);
 128                      }
 129  
 130                      let (constraint, _str_style) = p.parse_str();
 131  
 132                      if constraint.get().starts_with("=") {
 133                          cx.span_err(p.last_span, "input operand constraint contains '='");
 134                      } else if constraint.get().starts_with("+") {
 135                          cx.span_err(p.last_span, "input operand constraint contains '+'");
 136                      }
 137  
 138                      p.expect(&token::LPAREN);
 139                      let input = p.parse_expr();
 140                      p.expect(&token::RPAREN);
 141  
 142                      inputs.push((constraint, input));
 143                  }
 144              }
 145              Clobbers => {
 146                  let mut clobs = Vec::new();
 147                  while p.token != token::EOF &&
 148                        p.token != token::COLON &&
 149                        p.token != token::MOD_SEP {
 150  
 151                      if clobs.len() != 0 {
 152                          p.eat(&token::COMMA);
 153                      }
 154  
 155                      let (s, _str_style) = p.parse_str();
 156                      let clob = format!("~\\{{}\\}", s);
 157                      clobs.push(clob);
 158  
 159                      if OPTIONS.iter().any(|opt| s.equiv(opt)) {
 160                          cx.span_warn(p.last_span, "expected a clobber, but found an option");
 161                      }
 162                  }
 163  
 164                  cons = clobs.connect(",");
 165              }
 166              Options => {
 167                  let (option, _str_style) = p.parse_str();
 168  
 169                  if option.equiv(&("volatile")) {
 170                      // Indicates that the inline assembly has side effects
 171                      // and must not be optimized out along with its outputs.
 172                      volatile = true;
 173                  } else if option.equiv(&("alignstack")) {
 174                      alignstack = true;
 175                  } else if option.equiv(&("intel")) {
 176                      dialect = ast::AsmIntel;
 177                  } else {
 178                      cx.span_warn(p.last_span, "unrecognized option");
 179                  }
 180  
 181                  if p.token == token::COMMA {
 182                      p.eat(&token::COMMA);
 183                  }
 184              }
 185              StateNone => ()
 186          }
 187  
 188          loop {
 189              // MOD_SEP is a double colon '::' without space in between.
 190              // When encountered, the state must be advanced twice.
 191              match (&p.token, state.next(), state.next().next()) {
 192                  (&token::COLON, StateNone, _)   |
 193                  (&token::MOD_SEP, _, StateNone) => {
 194                      p.bump();
 195                      break 'statement;
 196                  }
 197                  (&token::COLON, st, _)   |
 198                  (&token::MOD_SEP, _, st) => {
 199                      p.bump();
 200                      state = st;
 201                  }
 202                  (&token::EOF, _, _) => break 'statement,
 203                  _ => break
 204              }
 205          }
 206      }
 207  
 208      // Append an input operand, with the form of ("0", expr)
 209      // that links to an output operand.
 210      for &(i, out) in read_write_operands.iter() {
 211          inputs.push((token::intern_and_get_ident(i.to_str()), out));
 212      }
 213  
 214      MacExpr::new(@ast::Expr {
 215          id: ast::DUMMY_NODE_ID,
 216          node: ast::ExprInlineAsm(ast::InlineAsm {
 217              asm: token::intern_and_get_ident(asm.get()),
 218              asm_str_style: asm_str_style.unwrap(),
 219              clobbers: token::intern_and_get_ident(cons),
 220              inputs: inputs,
 221              outputs: outputs,
 222              volatile: volatile,
 223              alignstack: alignstack,
 224              dialect: dialect
 225          }),
 226          span: sp
 227      })
 228  }