Rollup merge of #40702 - mrhota:global_asm, r=nagisa

Implement global_asm!() (RFC 1548)

This is a first attempt. ~~One (potential) problem I haven't solved is how to handle multiple usages of `global_asm!` in a module/crate. It looks like `LLVMSetModuleInlineAsm` overwrites module asm, and `LLVMAppendModuleInlineAsm` is not provided in LLVM C headers 😦~~

I can provide more detail as needed, but honestly, there's not a lot going on here.

r? @eddyb

CC @Amanieu @jackpot51

Tracking issue: #35119
This commit is contained in:
Corey Farwell 2017-04-14 17:41:03 -04:00 committed by GitHub
commit e6f6b445aa
48 changed files with 648 additions and 24 deletions

View File

@ -83,6 +83,7 @@
- [future_atomic_orderings](future-atomic-orderings.md)
- [generic_param_attrs](generic-param-attrs.md)
- [get_type_id](get-type-id.md)
- [global_asm](global_asm.md)
- [heap_api](heap-api.md)
- [i128](i128.md)
- [i128_type](i128-type.md)

View File

@ -189,3 +189,5 @@ constraints, etc.
[llvm-docs]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions
If you need more power and don't mind losing some of the niceties of
`asm!`, check out [global_asm](global_asm.html).

View File

@ -0,0 +1,78 @@
# `global_asm`
The tracking issue for this feature is: [#35119]
[#35119]: https://github.com/rust-lang/rust/issues/35119
------------------------
The `global_asm!` macro allows the programmer to write arbitrary
assembly outside the scope of a function body, passing it through
`rustc` and `llvm` to the assembler. The macro is a no-frills
interface to LLVM's concept of [module-level inline assembly]. That is,
all caveats applicable to LLVM's module-level inline assembly apply
to `global_asm!`.
[module-level inline assembly]: http://llvm.org/docs/LangRef.html#module-level-inline-assembly
`global_asm!` fills a role not currently satisfied by either `asm!`
or `#[naked]` functions. The programmer has _all_ features of the
assembler at their disposal. The linker will expect to resolve any
symbols defined in the inline assembly, modulo any symbols marked as
external. It also means syntax for directives and assembly follow the
conventions of the assembler in your toolchain.
A simple usage looks like this:
```rust,ignore
# #![feature(global_asm)]
# you also need relevant target_arch cfgs
global_asm!(include_str!("something_neato.s"));
```
And a more complicated usage looks like this:
```rust,ignore
# #![feature(global_asm)]
# #![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub mod sally {
global_asm!(r#"
.global foo
foo:
jmp baz
"#);
#[no_mangle]
pub unsafe extern "C" fn baz() {}
}
// the symbols `foo` and `bar` are global, no matter where
// `global_asm!` was used.
extern "C" {
fn foo();
fn bar();
}
pub mod harry {
global_asm!(r#"
.global bar
bar:
jmp quux
"#);
#[no_mangle]
pub unsafe extern "C" fn quux() {}
}
```
You may use `global_asm!` multiple times, anywhere in your crate, in
whatever way suits you. The effect is as if you concatenated all
usages and placed the larger, single usage in the crate root.
------------------------
If you don't need quite as much power and flexibility as
`global_asm!` provides, and you don't mind restricting your inline
assembly to `fn` bodies only, you might try the [asm](asm.html)
feature instead.

View File

@ -57,6 +57,8 @@ pub enum Def {
// Macro namespace
Macro(DefId, MacroKind),
GlobalAsm(DefId),
// Both namespaces
Err,
}
@ -144,7 +146,8 @@ impl Def {
Def::Variant(id) | Def::VariantCtor(id, ..) | Def::Enum(id) | Def::TyAlias(id) |
Def::AssociatedTy(id) | Def::TyParam(id) | Def::Struct(id) | Def::StructCtor(id, ..) |
Def::Union(id) | Def::Trait(id) | Def::Method(id) | Def::Const(id) |
Def::AssociatedConst(id) | Def::Local(id) | Def::Upvar(id, ..) | Def::Macro(id, ..) => {
Def::AssociatedConst(id) | Def::Local(id) | Def::Upvar(id, ..) | Def::Macro(id, ..) |
Def::GlobalAsm(id) => {
id
}
@ -185,6 +188,7 @@ impl Def {
Def::Label(..) => "label",
Def::SelfTy(..) => "self type",
Def::Macro(..) => "macro",
Def::GlobalAsm(..) => "global asm",
Def::Err => "unresolved item",
}
}

View File

@ -474,6 +474,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
visitor.visit_id(item.id);
walk_list!(visitor, visit_foreign_item, &foreign_module.items);
}
ItemGlobalAsm(_) => {
visitor.visit_id(item.id);
}
ItemTy(ref typ, ref type_parameters) => {
visitor.visit_id(item.id);
visitor.visit_ty(typ);

View File

@ -646,6 +646,13 @@ impl<'a> LoweringContext<'a> {
}
}
fn lower_global_asm(&mut self, ga: &GlobalAsm) -> P<hir::GlobalAsm> {
P(hir::GlobalAsm {
asm: ga.asm,
ctxt: ga.ctxt,
})
}
fn lower_variant(&mut self, v: &Variant) -> hir::Variant {
Spanned {
node: hir::Variant_ {
@ -1288,6 +1295,7 @@ impl<'a> LoweringContext<'a> {
}
ItemKind::Mod(ref m) => hir::ItemMod(self.lower_mod(m)),
ItemKind::ForeignMod(ref nm) => hir::ItemForeignMod(self.lower_foreign_mod(nm)),
ItemKind::GlobalAsm(ref ga) => hir::ItemGlobalAsm(self.lower_global_asm(ga)),
ItemKind::Ty(ref t, ref generics) => {
hir::ItemTy(self.lower_ty(t), self.lower_generics(generics))
}

View File

@ -109,6 +109,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
DefPathData::ValueNs(i.ident.name.as_str()),
ItemKind::MacroDef(..) => DefPathData::MacroDef(i.ident.name.as_str()),
ItemKind::Mac(..) => return self.visit_macro_invoc(i.id, false),
ItemKind::GlobalAsm(..) => DefPathData::Misc,
ItemKind::Use(ref view_path) => {
match view_path.node {
ViewPathGlob(..) => {}

View File

@ -1077,6 +1077,7 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String {
ItemFn(..) => "fn",
ItemMod(..) => "mod",
ItemForeignMod(..) => "foreign mod",
ItemGlobalAsm(..) => "global asm",
ItemTy(..) => "ty",
ItemEnum(..) => "enum",
ItemStruct(..) => "struct",

View File

@ -1495,6 +1495,12 @@ pub struct ForeignMod {
pub items: HirVec<ForeignItem>,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct GlobalAsm {
pub asm: Symbol,
pub ctxt: SyntaxContext,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct EnumDef {
pub variants: HirVec<Variant>,
@ -1686,6 +1692,8 @@ pub enum Item_ {
ItemMod(Mod),
/// An external module
ItemForeignMod(ForeignMod),
/// Module-level inline assembly (from global_asm!)
ItemGlobalAsm(P<GlobalAsm>),
/// A type alias, e.g. `type Foo = Bar<u8>`
ItemTy(P<Ty>, Generics),
/// An enum definition, e.g. `enum Foo<A, B> {C<A>, D<B>}`
@ -1720,6 +1728,7 @@ impl Item_ {
ItemFn(..) => "function",
ItemMod(..) => "module",
ItemForeignMod(..) => "foreign module",
ItemGlobalAsm(..) => "global asm",
ItemTy(..) => "type alias",
ItemEnum(..) => "enum",
ItemStruct(..) => "struct",

View File

@ -633,6 +633,11 @@ impl<'a> State<'a> {
self.print_foreign_mod(nmod, &item.attrs)?;
self.bclose(item.span)?;
}
hir::ItemGlobalAsm(ref ga) => {
self.head(&visibility_qualified(&item.vis, "global asm"))?;
word(&mut self.s, &ga.asm.as_str())?;
self.end()?
}
hir::ItemTy(ref ty, ref params) => {
self.ibox(indent_unit)?;
self.ibox(0)?;

View File

@ -881,6 +881,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for hir::Item {
hir::ItemFn(..) |
hir::ItemMod(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) |
hir::ItemEnum(..) |
hir::ItemStruct(..) |
@ -925,6 +926,7 @@ impl_stable_hash_for!(enum hir::Item_ {
ItemFn(fn_decl, unsafety, constness, abi, generics, body_id),
ItemMod(module),
ItemForeignMod(foreign_mod),
ItemGlobalAsm(global_asm),
ItemTy(ty, generics),
ItemEnum(enum_def, generics),
ItemStruct(variant_data, generics),
@ -1014,6 +1016,19 @@ impl_stable_hash_for!(struct hir::InlineAsmOutput {
is_indirect
});
impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for hir::GlobalAsm {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a, 'tcx>,
hasher: &mut StableHasher<W>) {
let hir::GlobalAsm {
asm,
ctxt: _
} = *self;
asm.hash_stable(hcx, hasher);
}
}
impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for hir::InlineAsm {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a, 'tcx>,
@ -1070,6 +1085,7 @@ impl_stable_hash_for!(enum hir::def::Def {
Upvar(def_id, index, expr_id),
Label(node_id),
Macro(def_id, macro_kind),
GlobalAsm(def_id),
Err
});

View File

@ -267,7 +267,8 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
hir::ItemMod(..) | hir::ItemForeignMod(..) |
hir::ItemImpl(..) | hir::ItemTrait(..) |
hir::ItemStruct(..) | hir::ItemEnum(..) |
hir::ItemUnion(..) | hir::ItemDefaultImpl(..) => {}
hir::ItemUnion(..) | hir::ItemDefaultImpl(..) |
hir::ItemGlobalAsm(..) => {}
}
}
hir_map::NodeTraitItem(trait_method) => {

View File

@ -314,7 +314,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
hir::ItemUse(..) |
hir::ItemMod(..) |
hir::ItemDefaultImpl(..) |
hir::ItemForeignMod(..) => {
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) => {
// These sorts of items have no lifetime parameters at all.
intravisit::walk_item(self, item);
}

View File

@ -233,6 +233,7 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
hir::ItemStatic(..) |
hir::ItemFn(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) => None,
hir::ItemEnum(..) |

View File

@ -507,6 +507,7 @@ extern "C" {
/// See Module::setModuleInlineAsm.
pub fn LLVMSetModuleInlineAsm(M: ModuleRef, Asm: *const c_char);
pub fn LLVMRustAppendModuleInlineAsm(M: ModuleRef, Asm: *const c_char);
/// See llvm::LLVMTypeKind::getTypeID.
pub fn LLVMRustGetTypeKind(Ty: TypeRef) -> TypeKind;

View File

@ -429,6 +429,7 @@ impl<'tcx> EntryKind<'tcx> {
EntryKind::Trait(_) => Def::Trait(did),
EntryKind::Enum(..) => Def::Enum(did),
EntryKind::MacroDef(_) => Def::Macro(did, MacroKind::Bang),
EntryKind::GlobalAsm => Def::GlobalAsm(did),
EntryKind::ForeignMod |
EntryKind::Impl(_) |

View File

@ -677,6 +677,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
return self.encode_info_for_mod(FromId(item.id, (m, &item.attrs, &item.vis)));
}
hir::ItemForeignMod(_) => EntryKind::ForeignMod,
hir::ItemGlobalAsm(..) => EntryKind::GlobalAsm,
hir::ItemTy(..) => EntryKind::Type,
hir::ItemEnum(..) => EntryKind::Enum(get_repr_options(&tcx, def_id)),
hir::ItemStruct(ref struct_def, _) => {
@ -917,6 +918,7 @@ impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> {
hir::ItemFn(..) |
hir::ItemMod(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemExternCrate(..) |
hir::ItemUse(..) |
hir::ItemDefaultImpl(..) |

View File

@ -267,6 +267,7 @@ pub enum EntryKind<'tcx> {
ForeignImmStatic,
ForeignMutStatic,
ForeignMod,
GlobalAsm,
Type,
Enum(ReprOptions),
Field,
@ -297,6 +298,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for EntryKind<'tcx> {
EntryKind::ForeignImmStatic |
EntryKind::ForeignMutStatic |
EntryKind::ForeignMod |
EntryKind::GlobalAsm |
EntryKind::Field |
EntryKind::Type => {
// Nothing else to hash here.

View File

@ -160,7 +160,10 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
self.prev_level
}
// Other `pub` items inherit levels from parents
_ => {
hir::ItemConst(..) | hir::ItemEnum(..) | hir::ItemExternCrate(..) |
hir::ItemGlobalAsm(..) | hir::ItemFn(..) | hir::ItemMod(..) |
hir::ItemStatic(..) | hir::ItemStruct(..) | hir::ItemTrait(..) |
hir::ItemTy(..) | hir::ItemUnion(..) | hir::ItemUse(..) => {
if item.vis == hir::Public { self.prev_level } else { None }
}
};
@ -212,7 +215,9 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
}
}
}
_ => {}
hir::ItemUse(..) | hir::ItemStatic(..) | hir::ItemConst(..) |
hir::ItemGlobalAsm(..) | hir::ItemTy(..) | hir::ItemMod(..) |
hir::ItemFn(..) | hir::ItemExternCrate(..) | hir::ItemDefaultImpl(..) => {}
}
// Mark all items in interfaces of reachable items as reachable
@ -225,6 +230,8 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
hir::ItemUse(..) => {}
// The interface is empty
hir::ItemDefaultImpl(..) => {}
// The interface is empty
hir::ItemGlobalAsm(..) => {}
// Visit everything
hir::ItemConst(..) | hir::ItemStatic(..) |
hir::ItemFn(..) | hir::ItemTy(..) => {
@ -1092,6 +1099,8 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx>
hir::ItemMod(..) => {}
// Checked in resolve
hir::ItemUse(..) => {}
// No subitems
hir::ItemGlobalAsm(..) => {}
// Subitems of these items have inherited publicity
hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) |
hir::ItemTy(..) => {

View File

@ -268,6 +268,8 @@ impl<'a> Resolver<'a> {
self.define(parent, ident, TypeNS, imported_binding);
}
ItemKind::GlobalAsm(..) => {}
ItemKind::Mod(..) if item.ident == keywords::Invalid.ident() => {} // Crate root
ItemKind::Mod(..) => {

View File

@ -1709,7 +1709,7 @@ impl<'a> Resolver<'a> {
}
}
ItemKind::ExternCrate(_) | ItemKind::MacroDef(..) => {
ItemKind::ExternCrate(_) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(_)=> {
// do nothing, these are just around to be encoded
}

View File

@ -341,6 +341,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
Def::AssociatedTy(..) |
Def::AssociatedConst(..) |
Def::PrimTy(_) |
Def::GlobalAsm(_) |
Def::Err => {
span_bug!(span,
"process_def_kind for unexpected item: {:?}",

View File

@ -701,6 +701,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
Def::SelfTy(..) |
Def::Label(..) |
Def::Macro(..) |
Def::GlobalAsm(..) |
Def::Err => None,
}
}

View File

@ -124,3 +124,11 @@ pub fn trans_inline_asm<'a, 'tcx>(
llvm::LLVMMDNodeInContext(bcx.ccx.llcx(), &val, 1));
}
}
pub fn trans_global_asm<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ga: &hir::GlobalAsm) {
let asm = CString::new(ga.asm.as_str().as_bytes()).unwrap();
unsafe {
llvm::LLVMRustAppendModuleInlineAsm(ccx.llmod(), asm.as_ptr());
}
}

View File

@ -349,6 +349,9 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>,
collect_neighbours(scx, instance, &mut neighbors);
}
TransItem::GlobalAsm(..) => {
recursion_depth_reset = None;
}
}
record_inlining_canditates(scx.tcx(), starting_point, &neighbors[..], inlining_map);
@ -840,6 +843,12 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
}
}
}
hir::ItemGlobalAsm(..) => {
debug!("RootCollector: ItemGlobalAsm({})",
def_id_to_string(self.scx.tcx(),
self.scx.tcx().hir.local_def_id(item.id)));
self.output.push(TransItem::GlobalAsm(item.id));
}
hir::ItemStatic(..) => {
debug!("RootCollector: ItemStatic({})",
def_id_to_string(self.scx.tcx(),

View File

@ -185,15 +185,16 @@ impl<'tcx> CodegenUnit<'tcx> {
symbol_name.len().hash(&mut state);
symbol_name.hash(&mut state);
let exported = match item {
TransItem::Fn(ref instance) => {
let node_id =
scx.tcx().hir.as_local_node_id(instance.def_id());
TransItem::Fn(ref instance) => {
let node_id =
scx.tcx().hir.as_local_node_id(instance.def_id());
node_id.map(|node_id| exported_symbols.contains(&node_id))
.unwrap_or(false)
}
TransItem::Static(node_id) => {
.unwrap_or(false)
}
TransItem::Static(node_id) => {
exported_symbols.contains(&node_id)
}
}
TransItem::GlobalAsm(..) => true,
};
exported.hash(&mut state);
}
@ -243,7 +244,9 @@ impl<'tcx> CodegenUnit<'tcx> {
TransItem::Fn(instance) => {
tcx.hir.as_local_node_id(instance.def_id())
}
TransItem::Static(node_id) => Some(node_id),
TransItem::Static(node_id) | TransItem::GlobalAsm(node_id) => {
Some(node_id)
}
}
}
}
@ -338,7 +341,8 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>,
None => {
match trans_item {
TransItem::Fn(..) |
TransItem::Static(..) => llvm::ExternalLinkage,
TransItem::Static(..) |
TransItem::GlobalAsm(..) => llvm::ExternalLinkage,
}
}
};
@ -483,7 +487,8 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't
Some(def_id)
}
TransItem::Static(node_id) => Some(tcx.hir.local_def_id(node_id)),
TransItem::Static(node_id) |
TransItem::GlobalAsm(node_id) => Some(tcx.hir.local_def_id(node_id)),
}
}

View File

@ -99,7 +99,10 @@ impl<'tcx> SymbolMap<'tcx> {
TransItem::Fn(Instance { def, .. }) => {
tcx.hir.as_local_node_id(def.def_id())
}
TransItem::Static(node_id) => Some(node_id),
TransItem::Static(node_id) |
TransItem::GlobalAsm(node_id) => {
Some(node_id)
}
}.map(|node_id| {
tcx.hir.span(node_id)
})

View File

@ -14,6 +14,7 @@
//! item-path. This is used for unit testing the code that generates
//! paths etc in all kinds of annoying scenarios.
use asm;
use attributes;
use base;
use consts;
@ -38,7 +39,8 @@ use std::iter;
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub enum TransItem<'tcx> {
Fn(Instance<'tcx>),
Static(NodeId)
Static(NodeId),
GlobalAsm(NodeId),
}
/// Describes how a translation item will be instantiated in object files.
@ -89,6 +91,14 @@ impl<'a, 'tcx> TransItem<'tcx> {
span_bug!(item.span, "Mismatch between hir::Item type and TransItem type")
}
}
TransItem::GlobalAsm(node_id) => {
let item = ccx.tcx().hir.expect_item(node_id);
if let hir::ItemGlobalAsm(ref ga) = item.node {
asm::trans_global_asm(ccx, ga);
} else {
span_bug!(item.span, "Mismatch between hir::Item type and TransItem type")
}
}
TransItem::Fn(instance) => {
let _task = ccx.tcx().dep_graph.in_task(
DepNode::TransCrateItem(instance.def_id())); // (*)
@ -123,6 +133,7 @@ impl<'a, 'tcx> TransItem<'tcx> {
TransItem::Fn(instance) => {
TransItem::predefine_fn(ccx, instance, linkage, &symbol_name);
}
TransItem::GlobalAsm(..) => {}
}
debug!("END PREDEFINING '{} ({})' in cgu {}",
@ -185,6 +196,10 @@ impl<'a, 'tcx> TransItem<'tcx> {
let def_id = scx.tcx().hir.local_def_id(node_id);
symbol_names::symbol_name(Instance::mono(scx.tcx(), def_id), scx)
}
TransItem::GlobalAsm(node_id) => {
let def_id = scx.tcx().hir.local_def_id(node_id);
format!("global_asm_{:?}", def_id)
}
}
}
@ -202,6 +217,7 @@ impl<'a, 'tcx> TransItem<'tcx> {
}
}
TransItem::Static(..) => InstantiationMode::GloballyShared,
TransItem::GlobalAsm(..) => InstantiationMode::GloballyShared,
}
}
@ -210,7 +226,8 @@ impl<'a, 'tcx> TransItem<'tcx> {
TransItem::Fn(ref instance) => {
instance.substs.types().next().is_some()
}
TransItem::Static(..) => false,
TransItem::Static(..) |
TransItem::GlobalAsm(..) => false,
}
}
@ -218,6 +235,7 @@ impl<'a, 'tcx> TransItem<'tcx> {
let def_id = match *self {
TransItem::Fn(ref instance) => instance.def_id(),
TransItem::Static(node_id) => tcx.hir.local_def_id(node_id),
TransItem::GlobalAsm(..) => return None,
};
let attributes = tcx.get_attrs(def_id);
@ -249,6 +267,9 @@ impl<'a, 'tcx> TransItem<'tcx> {
let instance = Instance::new(def_id, tcx.intern_substs(&[]));
to_string_internal(tcx, "static ", instance)
},
TransItem::GlobalAsm(..) => {
"global_asm".to_string()
}
};
fn to_string_internal<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@ -273,6 +294,9 @@ impl<'a, 'tcx> TransItem<'tcx> {
TransItem::Static(id) => {
format!("Static({:?})", id)
}
TransItem::GlobalAsm(id) => {
format!("GlobalAsm({:?})", id)
}
}
}
}

View File

@ -490,8 +490,10 @@ fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) {
let def_id = tcx.hir.local_def_id(item_id);
match it.node {
// These don't define types.
hir::ItemExternCrate(_) | hir::ItemUse(..) | hir::ItemMod(_) => {
}
hir::ItemExternCrate(_) |
hir::ItemUse(..) |
hir::ItemMod(_) |
hir::ItemGlobalAsm(_) => {}
hir::ItemForeignMod(ref foreign_mod) => {
for item in &foreign_mod.items {
let def_id = tcx.hir.local_def_id(item.id);
@ -543,12 +545,12 @@ fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) {
tcx.item_generics(def_id);
tcx.item_type(def_id);
tcx.item_predicates(def_id);
},
_ => {
}
hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => {
tcx.item_generics(def_id);
tcx.item_type(def_id);
tcx.item_predicates(def_id);
},
}
}
}
@ -1074,6 +1076,7 @@ fn ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ItemTrait(..) |
ItemMod(..) |
ItemForeignMod(..) |
ItemGlobalAsm(..) |
ItemExternCrate(..) |
ItemUse(..) => {
span_bug!(

View File

@ -113,6 +113,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
hir::ItemFn(..) |
hir::ItemMod(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) |
hir::ItemImpl(..) |
hir::ItemDefaultImpl(..) => {}

View File

@ -251,6 +251,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for TermsContext<'a, 'tcx> {
hir::ItemFn(..) |
hir::ItemMod(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) => {}
}
}

View File

@ -373,6 +373,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
// If we're inlining, skip private items.
_ if self.inlining && item.vis != hir::Public => {}
hir::ItemGlobalAsm(..) => {}
hir::ItemExternCrate(ref p) => {
let cstore = &self.cx.sess().cstore;
om.extern_crates.push(ExternCrate {

View File

@ -1585,6 +1585,15 @@ pub struct ForeignMod {
pub items: Vec<ForeignItem>,
}
/// Global inline assembly
///
/// aka module-level assembly or file-scoped assembly
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub struct GlobalAsm {
pub asm: Symbol,
pub ctxt: SyntaxContext,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct EnumDef {
pub variants: Vec<Variant>,
@ -1812,6 +1821,8 @@ pub enum ItemKind {
///
/// E.g. `extern {}` or `extern "C" {}`
ForeignMod(ForeignMod),
/// Module-level inline assembly (from `global_asm!()`)
GlobalAsm(P<GlobalAsm>),
/// A type alias (`type` or `pub type`).
///
/// E.g. `type Foo = Bar<u8>;`
@ -1864,6 +1875,7 @@ impl ItemKind {
ItemKind::Fn(..) => "function",
ItemKind::Mod(..) => "module",
ItemKind::ForeignMod(..) => "foreign module",
ItemKind::GlobalAsm(..) => "global asm",
ItemKind::Ty(..) => "type alias",
ItemKind::Enum(..) => "enum",
ItemKind::Struct(..) => "struct",

View File

@ -1039,6 +1039,7 @@ impl<'feat> ExpansionConfig<'feat> {
feature_tests! {
fn enable_quotes = quote,
fn enable_asm = asm,
fn enable_global_asm = global_asm,
fn enable_log_syntax = log_syntax,
fn enable_concat_idents = concat_idents,
fn enable_trace_macros = trace_macros,

View File

@ -346,6 +346,9 @@ declare_features! (
// Hack to document `-Z linker-flavor` in The Unstable Book
(active, linker_flavor, "1.18.0", Some(41142)),
// Allows module-level inline assembly by way of global_asm!()
(active, global_asm, "1.18.0", Some(35119)),
);
declare_features! (
@ -982,6 +985,9 @@ pub const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
pub const EXPLAIN_ASM: &'static str =
"inline assembly is not stable enough for use and is subject to change";
pub const EXPLAIN_GLOBAL_ASM: &'static str =
"`global_asm!` is not stable enough for use and is subject to change";
pub const EXPLAIN_LOG_SYNTAX: &'static str =
"`log_syntax!` is not stable enough for use and is subject to change";

View File

@ -140,6 +140,10 @@ pub trait Folder : Sized {
noop_fold_foreign_mod(nm, self)
}
fn fold_global_asm(&mut self, ga: P<GlobalAsm>) -> P<GlobalAsm> {
noop_fold_global_asm(ga, self)
}
fn fold_variant(&mut self, v: Variant) -> Variant {
noop_fold_variant(v, self)
}
@ -412,6 +416,11 @@ pub fn noop_fold_foreign_mod<T: Folder>(ForeignMod {abi, items}: ForeignMod,
}
}
pub fn noop_fold_global_asm<T: Folder>(ga: P<GlobalAsm>,
_: &mut T) -> P<GlobalAsm> {
ga
}
pub fn noop_fold_variant<T: Folder>(v: Variant, fld: &mut T) -> Variant {
Spanned {
node: Variant_ {
@ -867,6 +876,7 @@ pub fn noop_fold_item_kind<T: Folder>(i: ItemKind, folder: &mut T) -> ItemKind {
}
ItemKind::Mod(m) => ItemKind::Mod(folder.fold_mod(m)),
ItemKind::ForeignMod(nm) => ItemKind::ForeignMod(folder.fold_foreign_mod(nm)),
ItemKind::GlobalAsm(ga) => ItemKind::GlobalAsm(folder.fold_global_asm(ga)),
ItemKind::Ty(t, generics) => {
ItemKind::Ty(folder.fold_ty(t), folder.fold_generics(generics))
}

View File

@ -1267,6 +1267,11 @@ impl<'a> State<'a> {
self.print_foreign_mod(nmod, &item.attrs)?;
self.bclose(item.span)?;
}
ast::ItemKind::GlobalAsm(ref ga) => {
self.head(&visibility_qualified(&item.vis, "global_asm!"))?;
word(&mut self.s, &ga.asm.as_str())?;
self.end()?;
}
ast::ItemKind::Ty(ref ty, ref params) => {
self.ibox(INDENT_UNIT)?;
self.ibox(0)?;

View File

@ -58,6 +58,7 @@ pub trait Visitor<'ast>: Sized {
}
fn visit_mod(&mut self, m: &'ast Mod, _s: Span, _n: NodeId) { walk_mod(self, m) }
fn visit_foreign_item(&mut self, i: &'ast ForeignItem) { walk_foreign_item(self, i) }
fn visit_global_asm(&mut self, ga: &'ast GlobalAsm) { walk_global_asm(self, ga) }
fn visit_item(&mut self, i: &'ast Item) { walk_item(self, i) }
fn visit_local(&mut self, l: &'ast Local) { walk_local(self, l) }
fn visit_block(&mut self, b: &'ast Block) { walk_block(self, b) }
@ -253,6 +254,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
ItemKind::ForeignMod(ref foreign_module) => {
walk_list!(visitor, visit_foreign_item, &foreign_module.items);
}
ItemKind::GlobalAsm(ref ga) => visitor.visit_global_asm(ga),
ItemKind::Ty(ref typ, ref type_parameters) => {
visitor.visit_ty(typ);
visitor.visit_generics(type_parameters)
@ -464,6 +466,10 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, foreign_item: &'a
walk_list!(visitor, visit_attribute, &foreign_item.attrs);
}
pub fn walk_global_asm<'a, V: Visitor<'a>>(_: &mut V, _: &'a GlobalAsm) {
// Empty!
}
pub fn walk_ty_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a TyParamBound) {
match *bound {
TraitTyParamBound(ref typ, ref modifier) => {

View File

@ -0,0 +1,65 @@
// Copyright 2012-2013 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.
/// Module-level assembly support.
///
/// The macro defined here allows you to specify "top-level",
/// "file-scoped", or "module-level" assembly. These synonyms
/// all correspond to LLVM's module-level inline assembly instruction.
///
/// For example, `global_asm!("some assembly here")` translates to
/// LLVM's `module asm "some assembly here"`. All of LLVM's caveats
/// therefore apply.
use syntax::ast;
use syntax::ext::base;
use syntax::ext::base::*;
use syntax::feature_gate;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax_pos::Span;
use syntax::tokenstream;
use syntax::util::small_vector::SmallVector;
pub const MACRO: &'static str = "global_asm";
pub fn expand_global_asm<'cx>(cx: &'cx mut ExtCtxt,
sp: Span,
tts: &[tokenstream::TokenTree]) -> Box<base::MacResult + 'cx> {
if !cx.ecfg.enable_global_asm() {
feature_gate::emit_feature_err(&cx.parse_sess,
MACRO,
sp,
feature_gate::GateIssue::Language,
feature_gate::EXPLAIN_GLOBAL_ASM);
return DummyResult::any(sp);
}
let mut p = cx.new_parser_from_tts(tts);
let (asm, _) = match expr_to_string(cx,
panictry!(p.parse_expr()),
"inline assembly must be a string literal") {
Some((s, st)) => (s, st),
None => return DummyResult::any(sp),
};
MacEager::items(SmallVector::one(P(ast::Item {
ident: ast::Ident::with_empty_ctxt(Symbol::intern("")),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
node: ast::ItemKind::GlobalAsm(P(ast::GlobalAsm {
asm: asm,
ctxt: cx.backtrace(),
})),
vis: ast::Visibility::Inherited,
span: sp,
})))
}

View File

@ -38,6 +38,7 @@ mod concat_idents;
mod env;
mod format;
mod format_foreign;
mod global_asm;
mod log_syntax;
mod trace_macros;
@ -99,6 +100,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
module_path: expand_mod,
asm: asm::expand_asm,
global_asm: global_asm::expand_global_asm,
cfg: cfg::expand_cfg,
concat: concat::expand_syntax_ext,
concat_idents: concat_idents::expand_syntax_ext,

View File

@ -312,6 +312,10 @@ extern "C" LLVMValueRef LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString,
HasSideEffects, IsAlignStack, fromRust(Dialect)));
}
extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm) {
unwrap(M)->appendModuleInlineAsm(StringRef(Asm));
}
typedef DIBuilder *LLVMRustDIBuilderRef;
typedef struct LLVMOpaqueMetadata *LLVMRustMetadataRef;

3
src/test/codegen/foo.s Normal file
View File

@ -0,0 +1,3 @@
.global foo
foo:
jmp baz

View File

@ -0,0 +1,73 @@
// Copyright 2017 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.
// ignore-aarch64
// ignore-aarch64_be
// ignore-arm
// ignore-armeb
// ignore-avr
// ignore-bpfel
// ignore-bpfeb
// ignore-hexagon
// ignore-mips
// ignore-mipsel
// ignore-mips64
// ignore-mips64el
// ignore-msp430
// ignore-powerpc64
// ignore-powerpc64le
// ignore-powerpc
// ignore-r600
// ignore-amdgcn
// ignore-sparc
// ignore-sparcv9
// ignore-sparcel
// ignore-s390x
// ignore-tce
// ignore-thumb
// ignore-thumbeb
// ignore-xcore
// ignore-nvptx
// ignore-nvptx64
// ignore-le32
// ignore-le64
// ignore-amdil
// ignore-amdil64
// ignore-hsail
// ignore-hsail64
// ignore-spir
// ignore-spir64
// ignore-kalimba
// ignore-shave
// ignore-wasm32
// ignore-wasm64
// ignore-emscripten
// compile-flags: -C no-prepopulate-passes
#![feature(global_asm)]
#![crate_type = "lib"]
// CHECK-LABEL: foo
// CHECK: module asm
// this regex will capture the correct unconditional branch inst.
// CHECK: module asm "{{[[:space:]]+}}jmp baz"
global_asm!(r#"
.global foo
foo:
jmp baz
"#);
extern "C" {
fn foo();
}
// CHECK-LABEL: @baz
#[no_mangle]
pub unsafe extern "C" fn baz() {}

View File

@ -0,0 +1,68 @@
// Copyright 2017 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.
// ignore-aarch64
// ignore-aarch64_be
// ignore-arm
// ignore-armeb
// ignore-avr
// ignore-bpfel
// ignore-bpfeb
// ignore-hexagon
// ignore-mips
// ignore-mipsel
// ignore-mips64
// ignore-mips64el
// ignore-msp430
// ignore-powerpc64
// ignore-powerpc64le
// ignore-powerpc
// ignore-r600
// ignore-amdgcn
// ignore-sparc
// ignore-sparcv9
// ignore-sparcel
// ignore-s390x
// ignore-tce
// ignore-thumb
// ignore-thumbeb
// ignore-xcore
// ignore-nvptx
// ignore-nvptx64
// ignore-le32
// ignore-le64
// ignore-amdil
// ignore-amdil64
// ignore-hsail
// ignore-hsail64
// ignore-spir
// ignore-spir64
// ignore-kalimba
// ignore-shave
// ignore-wasm32
// ignore-wasm64
// ignore-emscripten
// compile-flags: -C no-prepopulate-passes
#![feature(global_asm)]
#![crate_type = "lib"]
// CHECK-LABEL: foo
// CHECK: module asm
// CHECK: module asm "{{[[:space:]]+}}jmp baz"
global_asm!(include_str!("foo.s"));
extern "C" {
fn foo();
}
// CHECK-LABEL: @baz
#[no_mangle]
pub unsafe extern "C" fn baz() {}

View File

@ -0,0 +1,90 @@
// Copyright 2017 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.
// ignore-aarch64
// ignore-aarch64_be
// ignore-arm
// ignore-armeb
// ignore-avr
// ignore-bpfel
// ignore-bpfeb
// ignore-hexagon
// ignore-mips
// ignore-mipsel
// ignore-mips64
// ignore-mips64el
// ignore-msp430
// ignore-powerpc64
// ignore-powerpc64le
// ignore-powerpc
// ignore-r600
// ignore-amdgcn
// ignore-sparc
// ignore-sparcv9
// ignore-sparcel
// ignore-s390x
// ignore-tce
// ignore-thumb
// ignore-thumbeb
// ignore-xcore
// ignore-nvptx
// ignore-nvptx64
// ignore-le32
// ignore-le64
// ignore-amdil
// ignore-amdil64
// ignore-hsail
// ignore-hsail64
// ignore-spir
// ignore-spir64
// ignore-kalimba
// ignore-shave
// ignore-wasm32
// ignore-wasm64
// ignore-emscripten
// compile-flags: -C no-prepopulate-passes
#![feature(global_asm)]
#![crate_type = "lib"]
#[no_std]
// CHECK-LABEL: foo
// CHECK: module asm
// CHECK: module asm "{{[[:space:]]+}}jmp baz"
// any other global_asm will be appended to this first block, so:
// CHECK-LABEL: bar
// CHECK: module asm "{{[[:space:]]+}}jmp quux"
global_asm!(r#"
.global foo
foo:
jmp baz
"#);
extern "C" {
fn foo();
}
// CHECK-LABEL: @baz
#[no_mangle]
pub unsafe extern "C" fn baz() {}
// no checks here; this has been appended to the first occurrence
global_asm!(r#"
.global bar
bar:
jmp quux
"#);
extern "C" {
fn bar();
}
#[no_mangle]
pub unsafe extern "C" fn quux() {}

View File

@ -0,0 +1,15 @@
// Copyright 2014 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.
// gate-test-global_asm
global_asm!(""); //~ ERROR `global_asm!` is not stable
fn main() {}

View File

@ -0,0 +1,28 @@
// Copyright 2017 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.
#![feature(global_asm)]
#[cfg(target_arch = "x86")]
global_asm!("");
#[cfg(target_arch = "x86_64")]
global_asm!("");
#[cfg(target_arch = "arm")]
global_asm!("");
#[cfg(target_arch = "aarch64")]
global_asm!("");
#[cfg(target_arch = "mips")]
global_asm!("");
fn main() {}

View File

@ -0,0 +1,31 @@
// Copyright 2017 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.
#![feature(global_asm)]
#![feature(naked_functions)]
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
global_asm!(r#"
.global foo
.global _foo
foo:
_foo:
ret
"#);
extern {
fn foo();
}
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
fn main() { unsafe { foo(); } }
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
fn main() {}