+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+# Translation of inline assembly.
+*/
+
+use core::prelude::*;
+
+use lib;
+use middle::trans::build::*;
+use middle::trans::callee;
+use middle::trans::common::*;
+use middle::ty;
+
+use syntax::ast;
+use syntax::ast::*;
+
+// Take an inline assembly expression and splat it out via LLVM
+pub fn trans_inline_asm(bcx: block, asm: @~str, ins: &[(@~str, @expr)], outs: &[(@~str, @expr)],
+ clobs: @~str, volatile: bool, alignstack: bool) -> block {
+
+ let mut bcx = bcx;
+ let mut constraints = ~[];
+ let mut cleanups = ~[];
+ let mut aoutputs = ~[];
+
+ // Prepare the output operands
+ let outputs = do outs.map |&(c, out)| {
+ constraints.push(copy *c);
+
+ let aoutty = ty::arg {
+ mode: ast::expl(ast::by_copy),
+ ty: expr_ty(bcx, out)
+ };
+ aoutputs.push(unpack_result!(bcx, {
+ callee::trans_arg_expr(bcx, aoutty, out, &mut cleanups, None, callee::DontAutorefArg)
+ }));
+
+ let e = match out.node {
+ ast::expr_addr_of(_, e) => e,
+ _ => fail!(~"Expression must be addr of")
+ };
+
+ let outty = ty::arg {
+ mode: ast::expl(ast::by_copy),
+ ty: expr_ty(bcx, e)
+ };
+
+ unpack_result!(bcx, {
+ callee::trans_arg_expr(bcx, outty, e, &mut cleanups, None, callee::DontAutorefArg)
+ })
+
+ };
+
+ for cleanups.each |c| {
+ revoke_clean(bcx, *c);
+ }
+ cleanups.clear();
+
+ // Now the input operands
+ let inputs = do ins.map |&(c, in)| {
+ constraints.push(copy *c);
+
+ let inty = ty::arg {
+ mode: ast::expl(ast::by_copy),
+ ty: expr_ty(bcx, in)
+ };
+
+ unpack_result!(bcx, {
+ callee::trans_arg_expr(bcx, inty, in, &mut cleanups, None, callee::DontAutorefArg)
+ })
+
+ };
+
+ for cleanups.each |c| {
+ revoke_clean(bcx, *c);
+ }
+
+ let mut constraints = str::connect(constraints, ",");
+
+ // Add the clobbers to our constraints list
+ if *clobs != ~"" && constraints != ~"" {
+ constraints += ~"," + *clobs;
+ } else {
+ constraints += *clobs;
+ }
+
+ debug!("Asm Constraints: %?", constraints);
+
+ let numOutputs = outputs.len();
+
+ // Depending on how many outputs we have, the return type is different
+ let output = if numOutputs == 0 {
+ T_void()
+ } else if numOutputs == 1 {
+ val_ty(outputs[0])
+ } else {
+ T_struct(outputs.map(|o| val_ty(*o)))
+ };
+
+ let r = do str::as_c_str(*asm) |a| {
+ do str::as_c_str(constraints) |c| {
+ // XXX: Allow selection of at&t or intel
+ InlineAsmCall(bcx, a, c, inputs, output, volatile, alignstack, lib::llvm::AD_ATT)
+ }
+ };
+
+ // Again, based on how many outputs we have
+ if numOutputs == 1 {
+ let op = PointerCast(bcx, aoutputs[0], T_ptr(val_ty(outputs[0])));
+ Store(bcx, r, op);
+ } else {
+ for aoutputs.eachi |i, o| {
+ let v = ExtractValue(bcx, r, i);
+ let op = PointerCast(bcx, *o, T_ptr(val_ty(outputs[i])));
+ Store(bcx, v, op);
+ }
+ }
+
+ return bcx;
+
+}