parent
0627c63ad9
commit
cba05a7a14
@ -276,7 +276,7 @@ fn build_clif_sysroot_for_triple(
|
|||||||
if channel == "release" {
|
if channel == "release" {
|
||||||
build_cmd.arg("--release");
|
build_cmd.arg("--release");
|
||||||
}
|
}
|
||||||
build_cmd.arg("--features").arg("compiler-builtins-no-asm backtrace panic-unwind");
|
build_cmd.arg("--features").arg("backtrace panic-unwind");
|
||||||
build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true");
|
build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true");
|
||||||
build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif");
|
build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif");
|
||||||
if compiler.triple.contains("apple") {
|
if compiler.triple.contains("apple") {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
never_type,
|
never_type,
|
||||||
linkage,
|
linkage,
|
||||||
extern_types,
|
extern_types,
|
||||||
|
naked_functions,
|
||||||
thread_local,
|
thread_local,
|
||||||
repr_simd,
|
repr_simd,
|
||||||
raw_ref_op
|
raw_ref_op
|
||||||
@ -340,6 +341,7 @@ struct ExternTypeWrapper {
|
|||||||
))]
|
))]
|
||||||
unsafe {
|
unsafe {
|
||||||
global_asm_test();
|
global_asm_test();
|
||||||
|
naked_test();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both statics have a reference that points to the same anonymous allocation.
|
// Both statics have a reference that points to the same anonymous allocation.
|
||||||
@ -395,6 +397,14 @@ fn stack_val_align() {
|
|||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64"))]
|
||||||
|
#[naked]
|
||||||
|
extern "C" fn naked_test() {
|
||||||
|
unsafe {
|
||||||
|
asm!("ret", options(noreturn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
enum c_void {
|
enum c_void {
|
||||||
_1,
|
_1,
|
||||||
|
37
src/base.rs
37
src/base.rs
@ -6,6 +6,7 @@
|
|||||||
use cranelift_module::ModuleError;
|
use cranelift_module::ModuleError;
|
||||||
use rustc_ast::InlineAsmOptions;
|
use rustc_ast::InlineAsmOptions;
|
||||||
use rustc_index::IndexVec;
|
use rustc_index::IndexVec;
|
||||||
|
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||||
use rustc_middle::ty::layout::FnAbiOf;
|
use rustc_middle::ty::layout::FnAbiOf;
|
||||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
@ -14,6 +15,7 @@
|
|||||||
|
|
||||||
use crate::constant::ConstantCx;
|
use crate::constant::ConstantCx;
|
||||||
use crate::debuginfo::{FunctionDebugContext, TypeDebugContext};
|
use crate::debuginfo::{FunctionDebugContext, TypeDebugContext};
|
||||||
|
use crate::inline_asm::codegen_naked_asm;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::pretty_clif::CommentWriter;
|
use crate::pretty_clif::CommentWriter;
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ pub(crate) fn codegen_fn<'tcx>(
|
|||||||
cached_func: Function,
|
cached_func: Function,
|
||||||
module: &mut dyn Module,
|
module: &mut dyn Module,
|
||||||
instance: Instance<'tcx>,
|
instance: Instance<'tcx>,
|
||||||
) -> CodegenedFunction {
|
) -> Option<CodegenedFunction> {
|
||||||
debug_assert!(!instance.args.has_infer());
|
debug_assert!(!instance.args.has_infer());
|
||||||
|
|
||||||
let symbol_name = tcx.symbol_name(instance).name.to_string();
|
let symbol_name = tcx.symbol_name(instance).name.to_string();
|
||||||
@ -48,6 +50,37 @@ pub(crate) fn codegen_fn<'tcx>(
|
|||||||
String::from_utf8_lossy(&buf).into_owned()
|
String::from_utf8_lossy(&buf).into_owned()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||||
|
assert_eq!(mir.basic_blocks.len(), 1);
|
||||||
|
assert!(mir.basic_blocks[START_BLOCK].statements.is_empty());
|
||||||
|
|
||||||
|
match &mir.basic_blocks[START_BLOCK].terminator().kind {
|
||||||
|
TerminatorKind::InlineAsm {
|
||||||
|
template,
|
||||||
|
operands,
|
||||||
|
options,
|
||||||
|
line_spans: _,
|
||||||
|
targets: _,
|
||||||
|
unwind: _,
|
||||||
|
} => {
|
||||||
|
codegen_naked_asm(
|
||||||
|
tcx,
|
||||||
|
cx,
|
||||||
|
module,
|
||||||
|
instance,
|
||||||
|
mir.basic_blocks[START_BLOCK].terminator().source_info.span,
|
||||||
|
&symbol_name,
|
||||||
|
template,
|
||||||
|
operands,
|
||||||
|
*options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// Declare function
|
// Declare function
|
||||||
let sig = get_function_sig(tcx, module.target_config().default_call_conv, instance);
|
let sig = get_function_sig(tcx, module.target_config().default_call_conv, instance);
|
||||||
let func_id = module.declare_function(&symbol_name, Linkage::Local, &sig).unwrap();
|
let func_id = module.declare_function(&symbol_name, Linkage::Local, &sig).unwrap();
|
||||||
@ -128,7 +161,7 @@ pub(crate) fn codegen_fn<'tcx>(
|
|||||||
// Verify function
|
// Verify function
|
||||||
verify_func(tcx, &clif_comments, &func);
|
verify_func(tcx, &clif_comments, &func);
|
||||||
|
|
||||||
CodegenedFunction { symbol_name, func_id, func, clif_comments, func_debug_cx }
|
Some(CodegenedFunction { symbol_name, func_id, func, clif_comments, func_debug_cx })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compile_fn(
|
pub(crate) fn compile_fn(
|
||||||
|
@ -482,15 +482,16 @@ fn module_codegen(
|
|||||||
for (mono_item, _) in mono_items {
|
for (mono_item, _) in mono_items {
|
||||||
match mono_item {
|
match mono_item {
|
||||||
MonoItem::Fn(inst) => {
|
MonoItem::Fn(inst) => {
|
||||||
let codegened_function = crate::base::codegen_fn(
|
if let Some(codegened_function) = crate::base::codegen_fn(
|
||||||
tcx,
|
tcx,
|
||||||
&mut cx,
|
&mut cx,
|
||||||
&mut type_dbg,
|
&mut type_dbg,
|
||||||
Function::new(),
|
Function::new(),
|
||||||
&mut module,
|
&mut module,
|
||||||
inst,
|
inst,
|
||||||
);
|
) {
|
||||||
codegened_functions.push(codegened_function);
|
codegened_functions.push(codegened_function);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MonoItem::Static(def_id) => {
|
MonoItem::Static(def_id) => {
|
||||||
let data_id = crate::constant::codegen_static(tcx, &mut module, def_id);
|
let data_id = crate::constant::codegen_static(tcx, &mut module, def_id);
|
||||||
|
@ -232,16 +232,16 @@ pub(crate) fn codegen_and_compile_fn<'tcx>(
|
|||||||
crate::PrintOnPanic(|| format!("{:?} {}", instance, tcx.symbol_name(instance).name));
|
crate::PrintOnPanic(|| format!("{:?} {}", instance, tcx.symbol_name(instance).name));
|
||||||
|
|
||||||
let cached_func = std::mem::replace(&mut cached_context.func, Function::new());
|
let cached_func = std::mem::replace(&mut cached_context.func, Function::new());
|
||||||
let codegened_func = crate::base::codegen_fn(
|
if let Some(codegened_func) = crate::base::codegen_fn(
|
||||||
tcx,
|
tcx,
|
||||||
cx,
|
cx,
|
||||||
&mut TypeDebugContext::default(),
|
&mut TypeDebugContext::default(),
|
||||||
cached_func,
|
cached_func,
|
||||||
module,
|
module,
|
||||||
instance,
|
instance,
|
||||||
);
|
) {
|
||||||
|
crate::base::compile_fn(cx, cached_context, module, codegened_func);
|
||||||
crate::base::compile_fn(cx, cached_context, module, codegened_func);
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
//! [`codegen_static`]: crate::constant::codegen_static
|
//! [`codegen_static`]: crate::constant::codegen_static
|
||||||
|
|
||||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||||
|
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||||
use rustc_middle::mir::mono::{MonoItem, MonoItemData};
|
use rustc_middle::mir::mono::{MonoItem, MonoItemData};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -33,7 +34,20 @@ fn predefine_mono_items<'tcx>(
|
|||||||
data.visibility,
|
data.visibility,
|
||||||
is_compiler_builtins,
|
is_compiler_builtins,
|
||||||
);
|
);
|
||||||
module.declare_function(name, linkage, &sig).unwrap();
|
let is_naked = tcx
|
||||||
|
.codegen_fn_attrs(instance.def_id())
|
||||||
|
.flags
|
||||||
|
.contains(CodegenFnAttrFlags::NAKED);
|
||||||
|
module
|
||||||
|
.declare_function(
|
||||||
|
name,
|
||||||
|
// Naked functions are defined in a separate object
|
||||||
|
// file from the codegen unit rustc expects them to
|
||||||
|
// be defined in.
|
||||||
|
if is_naked { Linkage::Import } else { linkage },
|
||||||
|
&sig,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
MonoItem::Static(_) | MonoItem::GlobalAsm(_) => {}
|
MonoItem::Static(_) | MonoItem::GlobalAsm(_) => {}
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,7 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>(
|
|||||||
stack_slots_input: Vec::new(),
|
stack_slots_input: Vec::new(),
|
||||||
stack_slots_output: Vec::new(),
|
stack_slots_output: Vec::new(),
|
||||||
stack_slot_size: Size::from_bytes(0),
|
stack_slot_size: Size::from_bytes(0),
|
||||||
|
is_naked: false,
|
||||||
};
|
};
|
||||||
asm_gen.allocate_registers();
|
asm_gen.allocate_registers();
|
||||||
asm_gen.allocate_stack_slots();
|
asm_gen.allocate_stack_slots();
|
||||||
@ -209,6 +210,121 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>(
|
|||||||
call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs);
|
call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn codegen_naked_asm<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
cx: &mut crate::CodegenCx,
|
||||||
|
module: &mut dyn Module,
|
||||||
|
instance: Instance<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
symbol_name: &str,
|
||||||
|
template: &[InlineAsmTemplatePiece],
|
||||||
|
operands: &[InlineAsmOperand<'tcx>],
|
||||||
|
options: InlineAsmOptions,
|
||||||
|
) {
|
||||||
|
// FIXME add .eh_frame unwind info directives
|
||||||
|
|
||||||
|
let operands = operands
|
||||||
|
.iter()
|
||||||
|
.map(|operand| match *operand {
|
||||||
|
InlineAsmOperand::In { .. }
|
||||||
|
| InlineAsmOperand::Out { .. }
|
||||||
|
| InlineAsmOperand::InOut { .. } => {
|
||||||
|
span_bug!(span, "invalid operand type for naked asm")
|
||||||
|
}
|
||||||
|
InlineAsmOperand::Const { ref value } => {
|
||||||
|
let cv = instance.instantiate_mir_and_normalize_erasing_regions(
|
||||||
|
tcx,
|
||||||
|
ty::ParamEnv::reveal_all(),
|
||||||
|
ty::EarlyBinder::bind(value.const_),
|
||||||
|
);
|
||||||
|
let const_value = cv
|
||||||
|
.eval(tcx, ty::ParamEnv::reveal_all(), value.span)
|
||||||
|
.expect("erroneous constant missed by mono item collection");
|
||||||
|
|
||||||
|
let value = rustc_codegen_ssa::common::asm_const_to_str(
|
||||||
|
tcx,
|
||||||
|
span,
|
||||||
|
const_value,
|
||||||
|
RevealAllLayoutCx(tcx).layout_of(cv.ty()),
|
||||||
|
);
|
||||||
|
CInlineAsmOperand::Const { value }
|
||||||
|
}
|
||||||
|
InlineAsmOperand::SymFn { ref value } => {
|
||||||
|
if cfg!(not(feature = "inline_asm_sym")) {
|
||||||
|
tcx.dcx()
|
||||||
|
.span_err(span, "asm! and global_asm! sym operands are not yet supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
let const_ = instance.instantiate_mir_and_normalize_erasing_regions(
|
||||||
|
tcx,
|
||||||
|
ty::ParamEnv::reveal_all(),
|
||||||
|
ty::EarlyBinder::bind(value.const_),
|
||||||
|
);
|
||||||
|
if let ty::FnDef(def_id, args) = *const_.ty().kind() {
|
||||||
|
let instance = ty::Instance::resolve_for_fn_ptr(
|
||||||
|
tcx,
|
||||||
|
ty::ParamEnv::reveal_all(),
|
||||||
|
def_id,
|
||||||
|
args,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let symbol = tcx.symbol_name(instance);
|
||||||
|
|
||||||
|
// Pass a wrapper rather than the function itself as the function itself may not
|
||||||
|
// be exported from the main codegen unit and may thus be unreachable from the
|
||||||
|
// object file created by an external assembler.
|
||||||
|
let inline_asm_index = cx.inline_asm_index.get();
|
||||||
|
cx.inline_asm_index.set(inline_asm_index + 1);
|
||||||
|
let wrapper_name = format!(
|
||||||
|
"__inline_asm_{}_wrapper_n{}",
|
||||||
|
cx.cgu_name.as_str().replace('.', "__").replace('-', "_"),
|
||||||
|
inline_asm_index
|
||||||
|
);
|
||||||
|
let sig =
|
||||||
|
get_function_sig(tcx, module.target_config().default_call_conv, instance);
|
||||||
|
create_wrapper_function(
|
||||||
|
module,
|
||||||
|
&mut cx.unwind_context,
|
||||||
|
sig,
|
||||||
|
&wrapper_name,
|
||||||
|
symbol.name,
|
||||||
|
);
|
||||||
|
|
||||||
|
CInlineAsmOperand::Symbol { symbol: wrapper_name }
|
||||||
|
} else {
|
||||||
|
span_bug!(span, "invalid type for asm sym (fn)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InlineAsmOperand::SymStatic { def_id } => {
|
||||||
|
assert!(tcx.is_static(def_id));
|
||||||
|
let instance = Instance::mono(tcx, def_id);
|
||||||
|
CInlineAsmOperand::Symbol { symbol: tcx.symbol_name(instance).name.to_owned() }
|
||||||
|
}
|
||||||
|
InlineAsmOperand::Label { .. } => {
|
||||||
|
span_bug!(span, "asm! label operands are not yet supported");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let asm_gen = InlineAssemblyGenerator {
|
||||||
|
tcx,
|
||||||
|
arch: tcx.sess.asm_arch.unwrap(),
|
||||||
|
enclosing_def_id: instance.def_id(),
|
||||||
|
template,
|
||||||
|
operands: &operands,
|
||||||
|
options,
|
||||||
|
registers: Vec::new(),
|
||||||
|
stack_slots_clobber: Vec::new(),
|
||||||
|
stack_slots_input: Vec::new(),
|
||||||
|
stack_slots_output: Vec::new(),
|
||||||
|
stack_slot_size: Size::from_bytes(0),
|
||||||
|
is_naked: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let generated_asm = asm_gen.generate_asm_wrapper(symbol_name);
|
||||||
|
cx.global_asm.push_str(&generated_asm);
|
||||||
|
}
|
||||||
|
|
||||||
struct InlineAssemblyGenerator<'a, 'tcx> {
|
struct InlineAssemblyGenerator<'a, 'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
arch: InlineAsmArch,
|
arch: InlineAsmArch,
|
||||||
@ -221,10 +337,13 @@ struct InlineAssemblyGenerator<'a, 'tcx> {
|
|||||||
stack_slots_input: Vec<Option<Size>>,
|
stack_slots_input: Vec<Option<Size>>,
|
||||||
stack_slots_output: Vec<Option<Size>>,
|
stack_slots_output: Vec<Option<Size>>,
|
||||||
stack_slot_size: Size,
|
stack_slot_size: Size,
|
||||||
|
is_naked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
|
impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
|
||||||
fn allocate_registers(&mut self) {
|
fn allocate_registers(&mut self) {
|
||||||
|
assert!(!self.is_naked);
|
||||||
|
|
||||||
let sess = self.tcx.sess;
|
let sess = self.tcx.sess;
|
||||||
let map = allocatable_registers(
|
let map = allocatable_registers(
|
||||||
self.arch,
|
self.arch,
|
||||||
@ -348,6 +467,8 @@ fn allocate_registers(&mut self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn allocate_stack_slots(&mut self) {
|
fn allocate_stack_slots(&mut self) {
|
||||||
|
assert!(!self.is_naked);
|
||||||
|
|
||||||
let mut slot_size = Size::from_bytes(0);
|
let mut slot_size = Size::from_bytes(0);
|
||||||
let mut slots_clobber = vec![None; self.operands.len()];
|
let mut slots_clobber = vec![None; self.operands.len()];
|
||||||
let mut slots_input = vec![None; self.operands.len()];
|
let mut slots_input = vec![None; self.operands.len()];
|
||||||
@ -468,30 +589,32 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
|
|||||||
if is_x86 {
|
if is_x86 {
|
||||||
generated_asm.push_str(".intel_syntax noprefix\n");
|
generated_asm.push_str(".intel_syntax noprefix\n");
|
||||||
}
|
}
|
||||||
Self::prologue(&mut generated_asm, self.arch);
|
if !self.is_naked {
|
||||||
|
Self::prologue(&mut generated_asm, self.arch);
|
||||||
|
|
||||||
// Save clobbered registers
|
// Save clobbered registers
|
||||||
if !self.options.contains(InlineAsmOptions::NORETURN) {
|
if !self.options.contains(InlineAsmOptions::NORETURN) {
|
||||||
|
for (reg, slot) in self
|
||||||
|
.registers
|
||||||
|
.iter()
|
||||||
|
.zip(self.stack_slots_clobber.iter().copied())
|
||||||
|
.filter_map(|(r, s)| r.zip(s))
|
||||||
|
{
|
||||||
|
Self::save_register(&mut generated_asm, self.arch, reg, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write input registers
|
||||||
for (reg, slot) in self
|
for (reg, slot) in self
|
||||||
.registers
|
.registers
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.stack_slots_clobber.iter().copied())
|
.zip(self.stack_slots_input.iter().copied())
|
||||||
.filter_map(|(r, s)| r.zip(s))
|
.filter_map(|(r, s)| r.zip(s))
|
||||||
{
|
{
|
||||||
Self::save_register(&mut generated_asm, self.arch, reg, slot);
|
Self::restore_register(&mut generated_asm, self.arch, reg, slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write input registers
|
|
||||||
for (reg, slot) in self
|
|
||||||
.registers
|
|
||||||
.iter()
|
|
||||||
.zip(self.stack_slots_input.iter().copied())
|
|
||||||
.filter_map(|(r, s)| r.zip(s))
|
|
||||||
{
|
|
||||||
Self::restore_register(&mut generated_asm, self.arch, reg, slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_x86 && self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
|
if is_x86 && self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
|
||||||
generated_asm.push_str(".att_syntax\n");
|
generated_asm.push_str(".att_syntax\n");
|
||||||
}
|
}
|
||||||
@ -553,30 +676,32 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
|
|||||||
generated_asm.push_str(".intel_syntax noprefix\n");
|
generated_asm.push_str(".intel_syntax noprefix\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.options.contains(InlineAsmOptions::NORETURN) {
|
if !self.is_naked {
|
||||||
// Read output registers
|
if !self.options.contains(InlineAsmOptions::NORETURN) {
|
||||||
for (reg, slot) in self
|
// Read output registers
|
||||||
.registers
|
for (reg, slot) in self
|
||||||
.iter()
|
.registers
|
||||||
.zip(self.stack_slots_output.iter().copied())
|
.iter()
|
||||||
.filter_map(|(r, s)| r.zip(s))
|
.zip(self.stack_slots_output.iter().copied())
|
||||||
{
|
.filter_map(|(r, s)| r.zip(s))
|
||||||
Self::save_register(&mut generated_asm, self.arch, reg, slot);
|
{
|
||||||
}
|
Self::save_register(&mut generated_asm, self.arch, reg, slot);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore clobbered registers
|
// Restore clobbered registers
|
||||||
for (reg, slot) in self
|
for (reg, slot) in self
|
||||||
.registers
|
.registers
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.stack_slots_clobber.iter().copied())
|
.zip(self.stack_slots_clobber.iter().copied())
|
||||||
.filter_map(|(r, s)| r.zip(s))
|
.filter_map(|(r, s)| r.zip(s))
|
||||||
{
|
{
|
||||||
Self::restore_register(&mut generated_asm, self.arch, reg, slot);
|
Self::restore_register(&mut generated_asm, self.arch, reg, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::epilogue(&mut generated_asm, self.arch);
|
Self::epilogue(&mut generated_asm, self.arch);
|
||||||
} else {
|
} else {
|
||||||
Self::epilogue_noreturn(&mut generated_asm, self.arch);
|
Self::epilogue_noreturn(&mut generated_asm, self.arch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_x86 {
|
if is_x86 {
|
||||||
|
Loading…
Reference in New Issue
Block a user