diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 15fc4317bf3a8..9a419c85894ad 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1346,11 +1346,11 @@ pub impl Liveness { self.propagate_through_expr(e, succ) } - expr_inline_asm(_, ref ins, ref outs, _, _, _) =>{ - let succ = do ins.foldr(succ) |&(_, expr), succ| { + expr_inline_asm(ref ia) =>{ + let succ = do ia.inputs.foldr(succ) |&(_, expr), succ| { self.propagate_through_expr(expr, succ) }; - do outs.foldr(succ) |&(_, expr), succ| { + do ia.outputs.foldr(succ) |&(_, expr), succ| { self.propagate_through_expr(expr, succ) } } @@ -1620,14 +1620,19 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) { visit::visit_expr(expr, self, vt); } - expr_inline_asm(_, ref ins, ref outs, _, _, _) => { - for ins.each |&(_, in)| { + expr_inline_asm(ref ia) => { + for ia.inputs.each |&(_, in)| { (vt.visit_expr)(in, self, vt); } // Output operands must be lvalues - for outs.each |&(_, out)| { - self.check_lvalue(out, vt); + for ia.outputs.each |&(_, out)| { + match out.node { + expr_addr_of(_, inner) => { + self.check_lvalue(inner, vt); + } + _ => {} + } (vt.visit_expr)(out, self, vt); } diff --git a/src/librustc/middle/trans/asm.rs b/src/librustc/middle/trans/asm.rs new file mode 100644 index 0000000000000..b3b23a8730d1a --- /dev/null +++ b/src/librustc/middle/trans/asm.rs @@ -0,0 +1,154 @@ +// 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 or the MIT license +// , 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; + +// Take an inline assembly expression and splat it out via LLVM +pub fn trans_inline_asm(bcx: block, ia: &ast::inline_asm) -> block { + + let mut bcx = bcx; + let mut constraints = ~[]; + let mut cleanups = ~[]; + let mut aoutputs = ~[]; + + // Prepare the output operands + let outputs = do ia.outputs.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 ia.inputs.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, ","); + + let mut clobbers = getClobbers(); + if *ia.clobbers != ~"" && clobbers != ~"" { + clobbers = *ia.clobbers + ~"," + clobbers; + } else { + clobbers += *ia.clobbers; + }; + + // Add the clobbers to our constraints list + if clobbers != ~"" && constraints != ~"" { + constraints += ~"," + clobbers; + } else { + constraints += clobbers; + } + + 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 dialect = match ia.dialect { + ast::asm_att => lib::llvm::AD_ATT, + ast::asm_intel => lib::llvm::AD_Intel + }; + + let r = do str::as_c_str(*ia.asm) |a| { + do str::as_c_str(constraints) |c| { + InlineAsmCall(bcx, a, c, inputs, output, ia.volatile, ia.alignstack, dialect) + } + }; + + // 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; + +} + +// Default per-arch clobbers +// Basically what clang does + +#[cfg(target_arch = "arm")] +#[cfg(target_arch = "mips")] +fn getClobbers() -> ~str { + ~"" +} + +#[cfg(target_arch = "x86")] +#[cfg(target_arch = "x86_64")] +fn getClobbers() -> ~str { + ~"~{dirflag},~{fpsr},~{flags}" +} diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 0da1a9acef212..9cd9916932302 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -127,6 +127,7 @@ use lib::llvm::{ValueRef, TypeRef, llvm, True}; use middle::borrowck::root_map_key; use middle::trans::_match; use middle::trans::adt; +use middle::trans::asm; use middle::trans::base; use middle::trans::base::*; use middle::trans::build::*; @@ -557,108 +558,8 @@ fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block { ast::expr_paren(a) => { return trans_rvalue_stmt_unadjusted(bcx, a); } - ast::expr_inline_asm(asm, ref ins, ref outs, - clobs, volatile, alignstack) => { - let mut constraints = ~[]; - let mut cleanups = ~[]; - let mut aoutputs = ~[]; - - 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 = ~[]; - - 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 - if *clobs != ~"" { - if constraints == ~"" { - constraints += *clobs; - } else { - constraints += ~"," + *clobs; - } - } else { - constraints += *clobs; - } - - debug!("Asm Constraints: %?", constraints); - - let output = if outputs.len() == 0 { - T_void() - } else if outputs.len() == 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| { - InlineAsmCall(bcx, a, c, inputs, output, volatile, - alignstack, lib::llvm::AD_ATT) - } - }; - - if outputs.len() == 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; + ast::expr_inline_asm(ref a) => { + return asm::trans_inline_asm(bcx, a); } _ => { bcx.tcx().sess.span_bug( diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs index cad2a03f7a1ef..cee92af6620b6 100644 --- a/src/librustc/middle/trans/type_use.rs +++ b/src/librustc/middle/trans/type_use.rs @@ -360,11 +360,11 @@ pub fn mark_for_expr(cx: Context, e: @expr) { mark_for_method_call(cx, e.id, e.callee_id); } - expr_inline_asm(_, ref ins, ref outs, _, _, _) => { - for ins.each |&(_, in)| { + expr_inline_asm(ref ia) => { + for ia.inputs.each |&(_, in)| { node_type_needs(cx, use_repr, in.id); } - for outs.each |&(_, out)| { + for ia.outputs.each |&(_, out)| { node_type_needs(cx, use_repr, out.id); } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 85efc9fa738e8..6d4af492c972e 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -2332,13 +2332,13 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, let region_lb = ty::re_scope(expr.id); instantiate_path(fcx, pth, tpt, expr.span, expr.id, region_lb); } - ast::expr_inline_asm(_, ref ins, ref outs, _, _, _) => { + ast::expr_inline_asm(ref ia) => { fcx.require_unsafe(expr.span, ~"use of inline assembly"); - for ins.each |&(_, in)| { + for ia.inputs.each |&(_, in)| { check_expr(fcx, in); } - for outs.each |&(_, out)| { + for ia.outputs.each |&(_, out)| { check_expr(fcx, out); } fcx.write_nil(id); diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc index e3df3b692fb87..456f9743afab4 100644 --- a/src/librustc/rustc.rc +++ b/src/librustc/rustc.rc @@ -80,6 +80,7 @@ pub mod middle { pub mod reachable; pub mod machine; pub mod adt; + pub mod asm; } pub mod ty; pub mod resolve; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 12d4e9d5e2447..c70288902a398 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -600,10 +600,7 @@ pub enum expr_ { expr_ret(Option<@expr>), expr_log(log_level, @expr, @expr), - expr_inline_asm(@~str, // asm - ~[(@~str, @expr)], // inputs - ~[(@~str, @expr)], // outputs - @~str, bool, bool), // clobbers, volatile, align stack + expr_inline_asm(inline_asm), expr_mac(mac), @@ -937,6 +934,27 @@ impl to_bytes::IterBytes for Ty { } } +#[auto_encode] +#[auto_decode] +#[deriving(Eq)] +pub enum asm_dialect { + asm_att, + asm_intel +} + +#[auto_encode] +#[auto_decode] +#[deriving(Eq)] +pub struct inline_asm { + asm: @~str, + clobbers: @~str, + inputs: ~[(@~str, @expr)], + outputs: ~[(@~str, @expr)], + volatile: bool, + alignstack: bool, + dialect: asm_dialect +} + #[auto_encode] #[auto_decode] #[deriving(Eq)] diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index a014d8ccb8bc5..b070948d4059f 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -53,13 +53,14 @@ pub fn expand_asm(cx: @ext_ctxt, sp: span, tts: &[ast::token_tree]) let mut cons = ~""; let mut volatile = false; let mut alignstack = false; + let mut dialect = ast::asm_att; let mut state = Asm; loop outer: { match state { Asm => { asm = expr_to_str(cx, p.parse_expr(), - ~"inline assembly must be a string literal."); + ~"inline assembly must be a string literal."); } Outputs => { while *p.token != token::EOF && @@ -125,6 +126,8 @@ pub fn expand_asm(cx: @ext_ctxt, sp: span, tts: &[ast::token_tree]) volatile = true; } else if option == ~"alignstack" { alignstack = true; + } else if option == ~"intel" { + dialect = ast::asm_intel; } if *p.token == token::COMMA { @@ -163,8 +166,15 @@ pub fn expand_asm(cx: @ext_ctxt, sp: span, tts: &[ast::token_tree]) MRExpr(@ast::expr { id: cx.next_id(), callee_id: cx.next_id(), - node: ast::expr_inline_asm(@asm, inputs, outputs, - @cons, volatile, alignstack), + node: ast::expr_inline_asm(ast::inline_asm { + asm: @asm, + clobbers: @cons, + inputs: inputs, + outputs: outputs, + volatile: volatile, + alignstack: alignstack, + dialect: dialect + }), span: sp }) } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9d4cf4e89392f..e54c495323f4e 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -559,13 +559,12 @@ pub fn noop_fold_expr(e: &expr_, fld: @ast_fold) -> expr_ { fld.fold_expr(e) ) } - expr_inline_asm(asm, ins, outs, c, v, a) => { - expr_inline_asm( - asm, - ins.map(|&(c, in)| (c, fld.fold_expr(in))), - outs.map(|&(c, out)| (c, fld.fold_expr(out))), - c, v, a - ) + expr_inline_asm(a) => { + expr_inline_asm(inline_asm { + inputs: a.inputs.map(|&(c, in)| (c, fld.fold_expr(in))), + outputs: a.outputs.map(|&(c, out)| (c, fld.fold_expr(out))), + .. a + }) } expr_mac(ref mac) => expr_mac(fold_mac((*mac))), expr_struct(path, ref fields, maybe_expr) => { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 9fffed7074b3d..3b56017b8e085 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1406,16 +1406,16 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { } } } - ast::expr_inline_asm(a, in, out, c, v, _) => { - if v { + ast::expr_inline_asm(a) => { + if a.volatile { word(s.s, ~"__volatile__ asm!"); } else { word(s.s, ~"asm!"); } popen(s); - print_string(s, *a); + print_string(s, *a.asm); word_space(s, ~":"); - for out.each |&(co, o)| { + for a.outputs.each |&(co, o)| { print_string(s, *co); popen(s); print_expr(s, o); @@ -1423,7 +1423,7 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { word_space(s, ~","); } word_space(s, ~":"); - for in.each |&(co, o)| { + for a.inputs.each |&(co, o)| { print_string(s, *co); popen(s); print_expr(s, o); @@ -1431,7 +1431,7 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { word_space(s, ~","); } word_space(s, ~":"); - print_string(s, *c); + print_string(s, *a.clobbers); pclose(s); } ast::expr_mac(ref m) => print_mac(s, (*m)), diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index a159c98d21b27..daa16e867b9da 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -565,11 +565,11 @@ pub fn visit_expr(ex: @expr, e: E, v: vt) { } expr_mac(ref mac) => visit_mac((*mac), e, v), expr_paren(x) => (v.visit_expr)(x, e, v), - expr_inline_asm(_, ins, outs, _, _, _) => { - for ins.each |&(_, in)| { + expr_inline_asm(ref a) => { + for a.inputs.each |&(_, in)| { (v.visit_expr)(in, e, v); } - for outs.each |&(_, out)| { + for a.outputs.each |&(_, out)| { (v.visit_expr)(out, e, v); } }