Sync rustc_codegen_cranelift 'ddd4ce2553
'
This commit is contained in:
commit
505aa48f24
43 changed files with 784 additions and 1243 deletions
|
@ -63,16 +63,16 @@ pub(crate) fn import_function<'tcx>(
|
|||
module: &mut dyn Module,
|
||||
inst: Instance<'tcx>,
|
||||
) -> FuncId {
|
||||
let name = tcx.symbol_name(inst).name.to_string();
|
||||
let name = tcx.symbol_name(inst).name;
|
||||
let sig = get_function_sig(tcx, module.isa().triple(), inst);
|
||||
module.declare_function(&name, Linkage::Import, &sig).unwrap()
|
||||
module.declare_function(name, Linkage::Import, &sig).unwrap()
|
||||
}
|
||||
|
||||
impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
||||
/// Instance must be monomorphized
|
||||
pub(crate) fn get_function_ref(&mut self, inst: Instance<'tcx>) -> FuncRef {
|
||||
let func_id = import_function(self.tcx, self.cx.module, inst);
|
||||
let func_ref = self.cx.module.declare_func_in_func(func_id, &mut self.bcx.func);
|
||||
let func_id = import_function(self.tcx, self.module, inst);
|
||||
let func_ref = self.module.declare_func_in_func(func_id, &mut self.bcx.func);
|
||||
|
||||
if self.clif_comments.enabled() {
|
||||
self.add_comment(func_ref, format!("{:?}", inst));
|
||||
|
@ -89,8 +89,8 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
|||
args: &[Value],
|
||||
) -> &[Value] {
|
||||
let sig = Signature { params, returns, call_conv: CallConv::triple_default(self.triple()) };
|
||||
let func_id = self.cx.module.declare_function(&name, Linkage::Import, &sig).unwrap();
|
||||
let func_ref = self.cx.module.declare_func_in_func(func_id, &mut self.bcx.func);
|
||||
let func_id = self.module.declare_function(name, Linkage::Import, &sig).unwrap();
|
||||
let func_ref = self.module.declare_func_in_func(func_id, &mut self.bcx.func);
|
||||
let call_inst = self.bcx.ins().call(func_ref, args);
|
||||
if self.clif_comments.enabled() {
|
||||
self.add_comment(call_inst, format!("easy_call {}", name));
|
||||
|
@ -295,7 +295,6 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
|
|||
pub(crate) fn codegen_terminator_call<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
span: Span,
|
||||
current_block: Block,
|
||||
func: &Operand<'tcx>,
|
||||
args: &[Operand<'tcx>],
|
||||
destination: Option<(Place<'tcx>, BasicBlock)>,
|
||||
|
@ -357,7 +356,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
.map(|inst| fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD))
|
||||
.unwrap_or(false);
|
||||
if is_cold {
|
||||
fx.cold_blocks.insert(current_block);
|
||||
// FIXME Mark current_block block as cold once Cranelift supports it
|
||||
}
|
||||
|
||||
// Unpack arguments tuple for closures
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_span::symbol::sym;
|
|||
pub(crate) fn codegen(
|
||||
tcx: TyCtxt<'_>,
|
||||
module: &mut impl Module,
|
||||
unwind_context: &mut UnwindContext<'_>,
|
||||
unwind_context: &mut UnwindContext,
|
||||
) -> bool {
|
||||
let any_dynamic_crate = tcx.dependency_formats(LOCAL_CRATE).iter().any(|(_, list)| {
|
||||
use rustc_middle::middle::dependency_format::Linkage;
|
||||
|
@ -29,7 +29,7 @@ pub(crate) fn codegen(
|
|||
|
||||
fn codegen_inner(
|
||||
module: &mut impl Module,
|
||||
unwind_context: &mut UnwindContext<'_>,
|
||||
unwind_context: &mut UnwindContext,
|
||||
kind: AllocatorKind,
|
||||
) {
|
||||
let usize_ty = module.target_config().pointer_type();
|
||||
|
|
|
@ -5,23 +5,23 @@ use std::convert::{TryFrom, TryInto};
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_session::Session;
|
||||
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_module::FuncId;
|
||||
use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct};
|
||||
|
||||
use object::write::*;
|
||||
use object::{RelocationEncoding, SectionKind, SymbolFlags};
|
||||
|
||||
use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct};
|
||||
|
||||
use gimli::SectionId;
|
||||
|
||||
use crate::debuginfo::{DebugReloc, DebugRelocName};
|
||||
|
||||
pub(crate) trait WriteMetadata {
|
||||
fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>, is_like_osx: bool);
|
||||
fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>);
|
||||
}
|
||||
|
||||
impl WriteMetadata for object::write::Object {
|
||||
fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>, _is_like_osx: bool) {
|
||||
fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>) {
|
||||
let segment = self.segment_name(object::write::StandardSegment::Data).to_vec();
|
||||
let section_id = self.add_section(segment, b".rustc".to_vec(), object::SectionKind::Data);
|
||||
let offset = self.append_section_data(section_id, &data, 1);
|
||||
|
@ -113,7 +113,7 @@ impl WriteDebugInfo for ObjectProduct {
|
|||
}
|
||||
|
||||
pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec<u8> {
|
||||
let triple = crate::build_isa(sess).triple().clone();
|
||||
let triple = crate::target_triple(sess);
|
||||
|
||||
let binary_format = match triple.binary_format {
|
||||
target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf,
|
||||
|
@ -141,13 +141,9 @@ pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object
|
|||
metadata_object.write().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn make_module(sess: &Session, name: String) -> ObjectModule {
|
||||
let mut builder = ObjectBuilder::new(
|
||||
crate::build_isa(sess),
|
||||
name + ".o",
|
||||
cranelift_module::default_libcall_names(),
|
||||
)
|
||||
.unwrap();
|
||||
pub(crate) fn make_module(sess: &Session, isa: Box<dyn TargetIsa>, name: String) -> ObjectModule {
|
||||
let mut builder =
|
||||
ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap();
|
||||
// Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size
|
||||
// is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections
|
||||
// can easily double the amount of time necessary to perform linking.
|
||||
|
|
|
@ -6,9 +6,14 @@ use rustc_middle::ty::adjustment::PointerCast;
|
|||
use rustc_middle::ty::layout::FnAbiExt;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
|
||||
use crate::constant::ConstantCx;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: Instance<'tcx>) {
|
||||
pub(crate) fn codegen_fn<'tcx>(
|
||||
cx: &mut crate::CodegenCx<'tcx>,
|
||||
module: &mut dyn Module,
|
||||
instance: Instance<'tcx>,
|
||||
) {
|
||||
let tcx = cx.tcx;
|
||||
|
||||
let _inst_guard =
|
||||
|
@ -18,9 +23,9 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
let mir = tcx.instance_mir(instance.def);
|
||||
|
||||
// Declare function
|
||||
let name = tcx.symbol_name(instance).name.to_string();
|
||||
let sig = get_function_sig(tcx, cx.module.isa().triple(), instance);
|
||||
let func_id = cx.module.declare_function(&name, Linkage::Local, &sig).unwrap();
|
||||
let symbol_name = tcx.symbol_name(instance);
|
||||
let sig = get_function_sig(tcx, module.isa().triple(), instance);
|
||||
let func_id = module.declare_function(symbol_name.name, Linkage::Local, &sig).unwrap();
|
||||
|
||||
cx.cached_context.clear();
|
||||
|
||||
|
@ -39,15 +44,19 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
(0..mir.basic_blocks().len()).map(|_| bcx.create_block()).collect();
|
||||
|
||||
// Make FunctionCx
|
||||
let pointer_type = cx.module.target_config().pointer_type();
|
||||
let pointer_type = module.target_config().pointer_type();
|
||||
let clif_comments = crate::pretty_clif::CommentWriter::new(tcx, instance);
|
||||
|
||||
let mut fx = FunctionCx {
|
||||
cx,
|
||||
module,
|
||||
tcx,
|
||||
pointer_type,
|
||||
vtables: FxHashMap::default(),
|
||||
constants_cx: ConstantCx::new(),
|
||||
|
||||
instance,
|
||||
symbol_name,
|
||||
mir,
|
||||
fn_abi: Some(FnAbi::of_instance(&RevealAllLayoutCx(tcx), instance, &[])),
|
||||
|
||||
|
@ -55,7 +64,6 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
block_map,
|
||||
local_map: IndexVec::with_capacity(mir.local_decls.len()),
|
||||
caller_location: None, // set by `codegen_fn_prelude`
|
||||
cold_blocks: EntitySet::new(),
|
||||
|
||||
clif_comments,
|
||||
source_info_set: indexmap::IndexSet::new(),
|
||||
|
@ -90,7 +98,8 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
let mut clif_comments = fx.clif_comments;
|
||||
let source_info_set = fx.source_info_set;
|
||||
let local_map = fx.local_map;
|
||||
let cold_blocks = fx.cold_blocks;
|
||||
|
||||
fx.constants_cx.finalize(fx.tcx, &mut *fx.module);
|
||||
|
||||
// Store function in context
|
||||
let context = &mut cx.cached_context;
|
||||
|
@ -103,21 +112,15 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
|
||||
// Perform rust specific optimizations
|
||||
tcx.sess.time("optimize clif ir", || {
|
||||
crate::optimize::optimize_function(
|
||||
tcx,
|
||||
instance,
|
||||
context,
|
||||
&cold_blocks,
|
||||
&mut clif_comments,
|
||||
);
|
||||
crate::optimize::optimize_function(tcx, instance, context, &mut clif_comments);
|
||||
});
|
||||
|
||||
// If the return block is not reachable, then the SSA builder may have inserted an `iconst.i128`
|
||||
// instruction, which doesn't have an encoding.
|
||||
context.compute_cfg();
|
||||
context.compute_domtree();
|
||||
context.eliminate_unreachable_code(cx.module.isa()).unwrap();
|
||||
context.dce(cx.module.isa()).unwrap();
|
||||
context.eliminate_unreachable_code(module.isa()).unwrap();
|
||||
context.dce(module.isa()).unwrap();
|
||||
// Some Cranelift optimizations expect the domtree to not yet be computed and as such don't
|
||||
// invalidate it when it would change.
|
||||
context.domtree.clear();
|
||||
|
@ -125,7 +128,6 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
context.want_disasm = crate::pretty_clif::should_write_ir(tcx);
|
||||
|
||||
// Define function
|
||||
let module = &mut cx.module;
|
||||
tcx.sess.time("define function", || {
|
||||
module
|
||||
.define_function(func_id, context, &mut NullTrapSink {}, &mut NullStackMapSink {})
|
||||
|
@ -136,7 +138,7 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
crate::pretty_clif::write_clif_file(
|
||||
tcx,
|
||||
"opt",
|
||||
Some(cx.module.isa()),
|
||||
Some(module.isa()),
|
||||
instance,
|
||||
&context,
|
||||
&clif_comments,
|
||||
|
@ -145,13 +147,13 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
if let Some(disasm) = &context.mach_compile_result.as_ref().unwrap().disasm {
|
||||
crate::pretty_clif::write_ir_file(
|
||||
tcx,
|
||||
&format!("{}.vcode", tcx.symbol_name(instance).name),
|
||||
|| format!("{}.vcode", tcx.symbol_name(instance).name),
|
||||
|file| file.write_all(disasm.as_bytes()),
|
||||
)
|
||||
}
|
||||
|
||||
// Define debuginfo for function
|
||||
let isa = cx.module.isa();
|
||||
let isa = module.isa();
|
||||
let debug_context = &mut cx.debug_context;
|
||||
let unwind_context = &mut cx.unwind_context;
|
||||
tcx.sess.time("generate debug info", || {
|
||||
|
@ -159,7 +161,7 @@ pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: In
|
|||
debug_context.define_function(
|
||||
instance,
|
||||
func_id,
|
||||
&name,
|
||||
symbol_name.name,
|
||||
isa,
|
||||
context,
|
||||
&source_info_set,
|
||||
|
@ -205,9 +207,8 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
|
|||
// Unwinding after panicking is not supported
|
||||
continue;
|
||||
|
||||
// FIXME once unwinding is supported uncomment next lines
|
||||
// // Unwinding is unlikely to happen, so mark cleanup block's as cold.
|
||||
// fx.cold_blocks.insert(block);
|
||||
// FIXME Once unwinding is supported and Cranelift supports marking blocks as cold, do
|
||||
// so for cleanup blocks.
|
||||
}
|
||||
|
||||
fx.bcx.ins().nop();
|
||||
|
@ -262,7 +263,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
|
|||
|
||||
let target = fx.get_block(*target);
|
||||
let failure = fx.bcx.create_block();
|
||||
fx.cold_blocks.insert(failure);
|
||||
// FIXME Mark failure block as cold once Cranelift supports it
|
||||
|
||||
if *expected {
|
||||
fx.bcx.ins().brz(cond, failure, &[]);
|
||||
|
@ -355,14 +356,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
|
|||
from_hir_call: _,
|
||||
} => {
|
||||
fx.tcx.sess.time("codegen call", || {
|
||||
crate::abi::codegen_terminator_call(
|
||||
fx,
|
||||
*fn_span,
|
||||
block,
|
||||
func,
|
||||
args,
|
||||
*destination,
|
||||
)
|
||||
crate::abi::codegen_terminator_call(fx, *fn_span, func, args, *destination)
|
||||
});
|
||||
}
|
||||
TerminatorKind::InlineAsm {
|
||||
|
@ -664,7 +658,7 @@ fn codegen_stmt<'tcx>(
|
|||
// FIXME use emit_small_memset where possible
|
||||
let addr = lval.to_ptr().get_addr(fx);
|
||||
let val = operand.load_scalar(fx);
|
||||
fx.bcx.call_memset(fx.cx.module.target_config(), addr, val, times);
|
||||
fx.bcx.call_memset(fx.module.target_config(), addr, val, times);
|
||||
} else {
|
||||
let loop_block = fx.bcx.create_block();
|
||||
let loop_block2 = fx.bcx.create_block();
|
||||
|
@ -750,85 +744,15 @@ fn codegen_stmt<'tcx>(
|
|||
| StatementKind::AscribeUserType(..) => {}
|
||||
|
||||
StatementKind::LlvmInlineAsm(asm) => {
|
||||
use rustc_span::symbol::Symbol;
|
||||
let LlvmInlineAsm { asm, outputs, inputs } = &**asm;
|
||||
let rustc_hir::LlvmInlineAsmInner {
|
||||
asm: asm_code, // Name
|
||||
outputs: output_names, // Vec<LlvmInlineAsmOutput>
|
||||
inputs: input_names, // Vec<Name>
|
||||
clobbers, // Vec<Name>
|
||||
volatile, // bool
|
||||
alignstack, // bool
|
||||
dialect: _,
|
||||
asm_str_style: _,
|
||||
} = asm;
|
||||
match asm_code.as_str().trim() {
|
||||
match asm.asm.asm.as_str().trim() {
|
||||
"" => {
|
||||
// Black box
|
||||
}
|
||||
"mov %rbx, %rsi\n cpuid\n xchg %rbx, %rsi" => {
|
||||
assert_eq!(input_names, &[Symbol::intern("{eax}"), Symbol::intern("{ecx}")]);
|
||||
assert_eq!(output_names.len(), 4);
|
||||
for (i, c) in (&["={eax}", "={esi}", "={ecx}", "={edx}"]).iter().enumerate() {
|
||||
assert_eq!(&output_names[i].constraint.as_str(), c);
|
||||
assert!(!output_names[i].is_rw);
|
||||
assert!(!output_names[i].is_indirect);
|
||||
}
|
||||
|
||||
assert_eq!(clobbers, &[]);
|
||||
|
||||
assert!(!volatile);
|
||||
assert!(!alignstack);
|
||||
|
||||
assert_eq!(inputs.len(), 2);
|
||||
let leaf = codegen_operand(fx, &inputs[0].1).load_scalar(fx); // %eax
|
||||
let subleaf = codegen_operand(fx, &inputs[1].1).load_scalar(fx); // %ecx
|
||||
|
||||
let (eax, ebx, ecx, edx) =
|
||||
crate::intrinsics::codegen_cpuid_call(fx, leaf, subleaf);
|
||||
|
||||
assert_eq!(outputs.len(), 4);
|
||||
codegen_place(fx, outputs[0])
|
||||
.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32)));
|
||||
codegen_place(fx, outputs[1])
|
||||
.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32)));
|
||||
codegen_place(fx, outputs[2])
|
||||
.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32)));
|
||||
codegen_place(fx, outputs[3])
|
||||
.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32)));
|
||||
}
|
||||
"xgetbv" => {
|
||||
assert_eq!(input_names, &[Symbol::intern("{ecx}")]);
|
||||
|
||||
assert_eq!(output_names.len(), 2);
|
||||
for (i, c) in (&["={eax}", "={edx}"]).iter().enumerate() {
|
||||
assert_eq!(&output_names[i].constraint.as_str(), c);
|
||||
assert!(!output_names[i].is_rw);
|
||||
assert!(!output_names[i].is_indirect);
|
||||
}
|
||||
|
||||
assert_eq!(clobbers, &[]);
|
||||
|
||||
assert!(!volatile);
|
||||
assert!(!alignstack);
|
||||
|
||||
crate::trap::trap_unimplemented(fx, "_xgetbv arch intrinsic is not supported");
|
||||
}
|
||||
// ___chkstk, ___chkstk_ms and __alloca are only used on Windows
|
||||
_ if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") => {
|
||||
crate::trap::trap_unimplemented(fx, "Stack probes are not supported");
|
||||
}
|
||||
_ if fx.tcx.symbol_name(fx.instance).name == "__alloca" => {
|
||||
crate::trap::trap_unimplemented(fx, "Alloca is not supported");
|
||||
}
|
||||
// Used in sys::windows::abort_internal
|
||||
"int $$0x29" => {
|
||||
crate::trap::trap_unimplemented(fx, "Windows abort");
|
||||
}
|
||||
_ => fx
|
||||
.tcx
|
||||
.sess
|
||||
.span_fatal(stmt.source_info.span, "Inline assembly is not supported"),
|
||||
_ => fx.tcx.sess.span_fatal(
|
||||
stmt.source_info.span,
|
||||
"Legacy `llvm_asm!` inline assembly is not supported. \
|
||||
Try using the new `asm!` instead.",
|
||||
),
|
||||
}
|
||||
}
|
||||
StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"),
|
||||
|
@ -844,7 +768,7 @@ fn codegen_stmt<'tcx>(
|
|||
let elem_size: u64 = pointee.size.bytes();
|
||||
let bytes =
|
||||
if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count };
|
||||
fx.bcx.call_memcpy(fx.cx.module.target_config(), dst, src, bytes);
|
||||
fx.bcx.call_memcpy(fx.module.target_config(), dst, src, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::ty::SymbolName;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::abi::{Integer, Primitive};
|
||||
use rustc_target::spec::{HasTargetSpec, Target};
|
||||
|
||||
use crate::constant::ConstantCx;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub(crate) fn pointer_ty(tcx: TyCtxt<'_>) -> types::Type {
|
||||
|
@ -226,12 +228,16 @@ pub(crate) fn type_sign(ty: Ty<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct FunctionCx<'m, 'clif, 'tcx> {
|
||||
pub(crate) cx: &'clif mut crate::CodegenCx<'m, 'tcx>,
|
||||
pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> {
|
||||
pub(crate) cx: &'clif mut crate::CodegenCx<'tcx>,
|
||||
pub(crate) module: &'m mut dyn Module,
|
||||
pub(crate) tcx: TyCtxt<'tcx>,
|
||||
pub(crate) pointer_type: Type, // Cached from module
|
||||
pub(crate) vtables: FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), DataId>,
|
||||
pub(crate) constants_cx: ConstantCx,
|
||||
|
||||
pub(crate) instance: Instance<'tcx>,
|
||||
pub(crate) symbol_name: SymbolName<'tcx>,
|
||||
pub(crate) mir: &'tcx Body<'tcx>,
|
||||
pub(crate) fn_abi: Option<FnAbi<'tcx, Ty<'tcx>>>,
|
||||
|
||||
|
@ -242,9 +248,6 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx> {
|
|||
/// When `#[track_caller]` is used, the implicit caller location is stored in this variable.
|
||||
pub(crate) caller_location: Option<CValue<'tcx>>,
|
||||
|
||||
/// See [`crate::optimize::code_layout`] for more information.
|
||||
pub(crate) cold_blocks: EntitySet<Block>,
|
||||
|
||||
pub(crate) clif_comments: crate::pretty_clif::CommentWriter,
|
||||
pub(crate) source_info_set: indexmap::IndexSet<SourceInfo>,
|
||||
|
||||
|
@ -339,7 +342,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
|||
}
|
||||
|
||||
pub(crate) fn triple(&self) -> &target_lexicon::Triple {
|
||||
self.cx.module.isa().triple()
|
||||
self.module.isa().triple()
|
||||
}
|
||||
|
||||
pub(crate) fn anonymous_str(&mut self, prefix: &str, msg: &str) -> Value {
|
||||
|
@ -352,15 +355,14 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
|||
let mut data_ctx = DataContext::new();
|
||||
data_ctx.define(msg.as_bytes().to_vec().into_boxed_slice());
|
||||
let msg_id = self
|
||||
.cx
|
||||
.module
|
||||
.declare_data(&format!("__{}_{:08x}", prefix, msg_hash), Linkage::Local, false, false)
|
||||
.unwrap();
|
||||
|
||||
// Ignore DuplicateDefinition error, as the data will be the same
|
||||
let _ = self.cx.module.define_data(msg_id, &data_ctx);
|
||||
let _ = self.module.define_data(msg_id, &data_ctx);
|
||||
|
||||
let local_msg_id = self.cx.module.declare_data_in_func(msg_id, self.bcx.func);
|
||||
let local_msg_id = self.module.declare_data_in_func(msg_id, self.bcx.func);
|
||||
if self.clif_comments.enabled() {
|
||||
self.add_comment(local_msg_id, msg);
|
||||
}
|
||||
|
|
107
compiler/rustc_codegen_cranelift/src/config.rs
Normal file
107
compiler/rustc_codegen_cranelift/src/config.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use std::env;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn bool_env_var(key: &str) -> bool {
|
||||
env::var(key).as_ref().map(|val| &**val) == Ok("1")
|
||||
}
|
||||
|
||||
/// The mode to use for compilation.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CodegenMode {
|
||||
/// AOT compile the crate. This is the default.
|
||||
Aot,
|
||||
/// JIT compile and execute the crate.
|
||||
Jit,
|
||||
/// JIT compile and execute the crate, but only compile functions the first time they are used.
|
||||
JitLazy,
|
||||
}
|
||||
|
||||
impl FromStr for CodegenMode {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"aot" => Ok(CodegenMode::Aot),
|
||||
"jit" => Ok(CodegenMode::Jit),
|
||||
"jit-lazy" => Ok(CodegenMode::JitLazy),
|
||||
_ => Err(format!("Unknown codegen mode `{}`", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration of cg_clif as passed in through `-Cllvm-args` and various env vars.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BackendConfig {
|
||||
/// Should the crate be AOT compiled or JIT executed.
|
||||
///
|
||||
/// Defaults to AOT compilation. Can be set using `-Cllvm-args=mode=...`.
|
||||
pub codegen_mode: CodegenMode,
|
||||
|
||||
/// When JIT mode is enable pass these arguments to the program.
|
||||
///
|
||||
/// Defaults to the value of `CG_CLIF_JIT_ARGS`.
|
||||
pub jit_args: Vec<String>,
|
||||
|
||||
/// Display the time it took to perform codegen for a crate.
|
||||
///
|
||||
/// Defaults to true when the `CG_CLIF_DISPLAY_CG_TIME` env var is set to 1 or false otherwise.
|
||||
/// Can be set using `-Cllvm-args=display_cg_time=...`.
|
||||
pub display_cg_time: bool,
|
||||
|
||||
/// Enable the Cranelift ir verifier for all compilation passes. If not set it will only run
|
||||
/// once before passing the clif ir to Cranelift for compilation.
|
||||
///
|
||||
/// Defaults to true when the `CG_CLIF_ENABLE_VERIFIER` env var is set to 1 or when cg_clif is
|
||||
/// compiled with debug assertions enabled or false otherwise. Can be set using
|
||||
/// `-Cllvm-args=enable_verifier=...`.
|
||||
pub enable_verifier: bool,
|
||||
|
||||
/// Don't cache object files in the incremental cache. Useful during development of cg_clif
|
||||
/// to make it possible to use incremental mode for all analyses performed by rustc without
|
||||
/// caching object files when their content should have been changed by a change to cg_clif.
|
||||
///
|
||||
/// Defaults to true when the `CG_CLIF_DISABLE_INCR_CACHE` env var is set to 1 or false
|
||||
/// otherwise. Can be set using `-Cllvm-args=disable_incr_cache=...`.
|
||||
pub disable_incr_cache: bool,
|
||||
}
|
||||
|
||||
impl Default for BackendConfig {
|
||||
fn default() -> Self {
|
||||
BackendConfig {
|
||||
codegen_mode: CodegenMode::Aot,
|
||||
jit_args: {
|
||||
let args = std::env::var("CG_CLIF_JIT_ARGS").unwrap_or_else(|_| String::new());
|
||||
args.split(' ').map(|arg| arg.to_string()).collect()
|
||||
},
|
||||
display_cg_time: bool_env_var("CG_CLIF_DISPLAY_CG_TIME"),
|
||||
enable_verifier: cfg!(debug_assertions) || bool_env_var("CG_CLIF_ENABLE_VERIFIER"),
|
||||
disable_incr_cache: bool_env_var("CG_CLIF_DISABLE_INCR_CACHE"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendConfig {
|
||||
/// Parse the configuration passed in using `-Cllvm-args`.
|
||||
pub fn from_opts(opts: &[String]) -> Result<Self, String> {
|
||||
fn parse_bool(name: &str, value: &str) -> Result<bool, String> {
|
||||
value.parse().map_err(|_| format!("failed to parse value `{}` for {}", value, name))
|
||||
}
|
||||
|
||||
let mut config = BackendConfig::default();
|
||||
for opt in opts {
|
||||
if let Some((name, value)) = opt.split_once('=') {
|
||||
match name {
|
||||
"mode" => config.codegen_mode = value.parse()?,
|
||||
"display_cg_time" => config.display_cg_time = parse_bool(name, value)?,
|
||||
"enable_verifier" => config.enable_verifier = parse_bool(name, value)?,
|
||||
"disable_incr_cache" => config.disable_incr_cache = parse_bool(name, value)?,
|
||||
_ => return Err(format!("Unknown option `{}`", name)),
|
||||
}
|
||||
} else {
|
||||
return Err(format!("Invalid option `{}`", opt));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::interpret::{
|
||||
|
@ -15,10 +15,10 @@ use cranelift_module::*;
|
|||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ConstantCx {
|
||||
todo: Vec<TodoItem>,
|
||||
done: FxHashSet<DataId>,
|
||||
anon_allocs: FxHashMap<AllocId, DataId>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -28,6 +28,10 @@ enum TodoItem {
|
|||
}
|
||||
|
||||
impl ConstantCx {
|
||||
pub(crate) fn new() -> Self {
|
||||
ConstantCx { todo: vec![], done: FxHashSet::default(), anon_allocs: FxHashMap::default() }
|
||||
}
|
||||
|
||||
pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut dyn Module) {
|
||||
//println!("todo {:?}", self.todo);
|
||||
define_all_allocs(tcx, module, &mut self);
|
||||
|
@ -74,8 +78,10 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool {
|
|||
all_constants_ok
|
||||
}
|
||||
|
||||
pub(crate) fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) {
|
||||
pub(crate) fn codegen_static(tcx: TyCtxt<'_>, module: &mut dyn Module, def_id: DefId) {
|
||||
let mut constants_cx = ConstantCx::new();
|
||||
constants_cx.todo.push(TodoItem::Static(def_id));
|
||||
constants_cx.finalize(tcx, module);
|
||||
}
|
||||
|
||||
pub(crate) fn codegen_tls_ref<'tcx>(
|
||||
|
@ -83,8 +89,8 @@ pub(crate) fn codegen_tls_ref<'tcx>(
|
|||
def_id: DefId,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> CValue<'tcx> {
|
||||
let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false);
|
||||
let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
|
||||
let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
if fx.clif_comments.enabled() {
|
||||
fx.add_comment(local_data_id, format!("tls {:?}", def_id));
|
||||
}
|
||||
|
@ -97,8 +103,8 @@ fn codegen_static_ref<'tcx>(
|
|||
def_id: DefId,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> CPlace<'tcx> {
|
||||
let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false);
|
||||
let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
|
||||
let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
if fx.clif_comments.enabled() {
|
||||
fx.add_comment(local_data_id, format!("{:?}", def_id));
|
||||
}
|
||||
|
@ -182,28 +188,31 @@ pub(crate) fn codegen_const_value<'tcx>(
|
|||
let alloc_kind = fx.tcx.get_global_alloc(ptr.alloc_id);
|
||||
let base_addr = match alloc_kind {
|
||||
Some(GlobalAlloc::Memory(alloc)) => {
|
||||
fx.cx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id));
|
||||
let data_id =
|
||||
data_id_for_alloc_id(fx.cx.module, ptr.alloc_id, alloc.mutability);
|
||||
fx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id));
|
||||
let data_id = data_id_for_alloc_id(
|
||||
&mut fx.constants_cx,
|
||||
fx.module,
|
||||
ptr.alloc_id,
|
||||
alloc.mutability,
|
||||
);
|
||||
let local_data_id =
|
||||
fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
if fx.clif_comments.enabled() {
|
||||
fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id));
|
||||
}
|
||||
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
|
||||
}
|
||||
Some(GlobalAlloc::Function(instance)) => {
|
||||
let func_id =
|
||||
crate::abi::import_function(fx.tcx, fx.cx.module, instance);
|
||||
let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
|
||||
let local_func_id =
|
||||
fx.cx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
|
||||
fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
|
||||
fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
|
||||
}
|
||||
Some(GlobalAlloc::Static(def_id)) => {
|
||||
assert!(fx.tcx.is_static(def_id));
|
||||
let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false);
|
||||
let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
|
||||
let local_data_id =
|
||||
fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
if fx.clif_comments.enabled() {
|
||||
fx.add_comment(local_data_id, format!("{:?}", def_id));
|
||||
}
|
||||
|
@ -243,10 +252,11 @@ fn pointer_for_allocation<'tcx>(
|
|||
alloc: &'tcx Allocation,
|
||||
) -> crate::pointer::Pointer {
|
||||
let alloc_id = fx.tcx.create_memory_alloc(alloc);
|
||||
fx.cx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
|
||||
let data_id = data_id_for_alloc_id(fx.cx.module, alloc_id, alloc.mutability);
|
||||
fx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
|
||||
let data_id =
|
||||
data_id_for_alloc_id(&mut fx.constants_cx, &mut *fx.module, alloc_id, alloc.mutability);
|
||||
|
||||
let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
if fx.clif_comments.enabled() {
|
||||
fx.add_comment(local_data_id, format!("{:?}", alloc_id));
|
||||
}
|
||||
|
@ -255,18 +265,14 @@ fn pointer_for_allocation<'tcx>(
|
|||
}
|
||||
|
||||
fn data_id_for_alloc_id(
|
||||
cx: &mut ConstantCx,
|
||||
module: &mut dyn Module,
|
||||
alloc_id: AllocId,
|
||||
mutability: rustc_hir::Mutability,
|
||||
) -> DataId {
|
||||
module
|
||||
.declare_data(
|
||||
&format!(".L__alloc_{:x}", alloc_id.0),
|
||||
Linkage::Local,
|
||||
mutability == rustc_hir::Mutability::Mut,
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
*cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
|
||||
module.declare_anonymous_data(mutability == rustc_hir::Mutability::Mut, false).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
fn data_id_for_static(
|
||||
|
@ -344,7 +350,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
|
|||
GlobalAlloc::Memory(alloc) => alloc,
|
||||
GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(),
|
||||
};
|
||||
let data_id = data_id_for_alloc_id(module, alloc_id, alloc.mutability);
|
||||
let data_id = data_id_for_alloc_id(cx, module, alloc_id, alloc.mutability);
|
||||
(data_id, alloc, None)
|
||||
}
|
||||
TodoItem::Static(def_id) => {
|
||||
|
@ -397,7 +403,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
|
|||
}
|
||||
GlobalAlloc::Memory(target_alloc) => {
|
||||
cx.todo.push(TodoItem::Alloc(reloc));
|
||||
data_id_for_alloc_id(module, reloc, target_alloc.mutability)
|
||||
data_id_for_alloc_id(cx, module, reloc, target_alloc.mutability)
|
||||
}
|
||||
GlobalAlloc::Static(def_id) => {
|
||||
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
|
||||
|
@ -419,8 +425,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
|
|||
data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
|
||||
}
|
||||
|
||||
// FIXME don't duplicate definitions in lazy jit mode
|
||||
let _ = module.define_data(data_id, &data_ctx);
|
||||
module.define_data(data_id, &data_ctx).unwrap();
|
||||
cx.done.insert(data_id);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,17 +5,19 @@ use crate::prelude::*;
|
|||
use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa};
|
||||
|
||||
use gimli::write::{Address, CieId, EhFrame, FrameTable, Section};
|
||||
use gimli::RunTimeEndian;
|
||||
|
||||
use crate::backend::WriteDebugInfo;
|
||||
|
||||
pub(crate) struct UnwindContext<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
pub(crate) struct UnwindContext {
|
||||
endian: RunTimeEndian,
|
||||
frame_table: FrameTable,
|
||||
cie_id: Option<CieId>,
|
||||
}
|
||||
|
||||
impl<'tcx> UnwindContext<'tcx> {
|
||||
pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa, pic_eh_frame: bool) -> Self {
|
||||
impl UnwindContext {
|
||||
pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa, pic_eh_frame: bool) -> Self {
|
||||
let endian = super::target_endian(tcx);
|
||||
let mut frame_table = FrameTable::default();
|
||||
|
||||
let cie_id = if let Some(mut cie) = isa.create_systemv_cie() {
|
||||
|
@ -28,7 +30,7 @@ impl<'tcx> UnwindContext<'tcx> {
|
|||
None
|
||||
};
|
||||
|
||||
UnwindContext { tcx, frame_table, cie_id }
|
||||
UnwindContext { endian, frame_table, cie_id }
|
||||
}
|
||||
|
||||
pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: &dyn TargetIsa) {
|
||||
|
@ -54,8 +56,7 @@ impl<'tcx> UnwindContext<'tcx> {
|
|||
}
|
||||
|
||||
pub(crate) fn emit<P: WriteDebugInfo>(self, product: &mut P) {
|
||||
let mut eh_frame =
|
||||
EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.tcx)));
|
||||
let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(self.endian));
|
||||
self.frame_table.write_eh_frame(&mut eh_frame).unwrap();
|
||||
|
||||
if !eh_frame.0.writer.slice().is_empty() {
|
||||
|
@ -71,16 +72,12 @@ impl<'tcx> UnwindContext<'tcx> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "jit")]
|
||||
pub(crate) unsafe fn register_jit(
|
||||
self,
|
||||
jit_module: &cranelift_jit::JITModule,
|
||||
) -> Option<UnwindRegistry> {
|
||||
let mut eh_frame =
|
||||
EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.tcx)));
|
||||
pub(crate) unsafe fn register_jit(self, jit_module: &cranelift_jit::JITModule) {
|
||||
let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(self.endian));
|
||||
self.frame_table.write_eh_frame(&mut eh_frame).unwrap();
|
||||
|
||||
if eh_frame.0.writer.slice().is_empty() {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut eh_frame = eh_frame.0.relocate_for_jit(jit_module);
|
||||
|
@ -88,7 +85,10 @@ impl<'tcx> UnwindContext<'tcx> {
|
|||
// GCC expects a terminating "empty" length, so write a 0 length at the end of the table.
|
||||
eh_frame.extend(&[0, 0, 0, 0]);
|
||||
|
||||
let mut registrations = Vec::new();
|
||||
// FIXME support unregistering unwind tables once cranelift-jit supports deallocating
|
||||
// individual functions
|
||||
#[allow(unused_variables)]
|
||||
let (eh_frame, eh_frame_len, _) = Vec::into_raw_parts(eh_frame);
|
||||
|
||||
// =======================================================================
|
||||
// Everything after this line up to the end of the file is loosly based on
|
||||
|
@ -96,8 +96,8 @@ impl<'tcx> UnwindContext<'tcx> {
|
|||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
// On macOS, `__register_frame` takes a pointer to a single FDE
|
||||
let start = eh_frame.as_ptr();
|
||||
let end = start.add(eh_frame.len());
|
||||
let start = eh_frame;
|
||||
let end = start.add(eh_frame_len);
|
||||
let mut current = start;
|
||||
|
||||
// Walk all of the entries in the frame table and register them
|
||||
|
@ -107,7 +107,6 @@ impl<'tcx> UnwindContext<'tcx> {
|
|||
// Skip over the CIE
|
||||
if current != start {
|
||||
__register_frame(current);
|
||||
registrations.push(current as usize);
|
||||
}
|
||||
|
||||
// Move to the next table entry (+4 because the length itself is not inclusive)
|
||||
|
@ -117,41 +116,12 @@ impl<'tcx> UnwindContext<'tcx> {
|
|||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
// On other platforms, `__register_frame` will walk the FDEs until an entry of length 0
|
||||
let ptr = eh_frame.as_ptr();
|
||||
__register_frame(ptr);
|
||||
registrations.push(ptr as usize);
|
||||
__register_frame(eh_frame);
|
||||
}
|
||||
|
||||
Some(UnwindRegistry { _frame_table: eh_frame, registrations })
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a registry of function unwind information for System V ABI.
|
||||
pub(crate) struct UnwindRegistry {
|
||||
_frame_table: Vec<u8>,
|
||||
registrations: Vec<usize>,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// libunwind import
|
||||
fn __register_frame(fde: *const u8);
|
||||
fn __deregister_frame(fde: *const u8);
|
||||
}
|
||||
|
||||
impl Drop for UnwindRegistry {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// libgcc stores the frame entries as a linked list in decreasing sort order
|
||||
// based on the PC value of the registered entry.
|
||||
//
|
||||
// As we store the registrations in increasing order, it would be O(N^2) to
|
||||
// deregister in that order.
|
||||
//
|
||||
// To ensure that we just pop off the first element in the list upon every
|
||||
// deregistration, walk our list of registrations backwards.
|
||||
for fde in self.registrations.iter().rev() {
|
||||
__deregister_frame(*fde as *const _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,6 @@ use cranelift_object::ObjectModule;
|
|||
|
||||
use crate::{prelude::*, BackendConfig};
|
||||
|
||||
fn new_module(tcx: TyCtxt<'_>, name: String) -> ObjectModule {
|
||||
let module = crate::backend::make_module(tcx.sess, name);
|
||||
assert_eq!(pointer_ty(tcx), module.target_config().pointer_type());
|
||||
module
|
||||
}
|
||||
|
||||
struct ModuleCodegenResult(CompiledModule, Option<(WorkProductId, WorkProduct)>);
|
||||
|
||||
impl<HCX> HashStable<HCX> for ModuleCodegenResult {
|
||||
|
@ -32,11 +26,12 @@ impl<HCX> HashStable<HCX> for ModuleCodegenResult {
|
|||
|
||||
fn emit_module(
|
||||
tcx: TyCtxt<'_>,
|
||||
backend_config: &BackendConfig,
|
||||
name: String,
|
||||
kind: ModuleKind,
|
||||
module: ObjectModule,
|
||||
debug: Option<DebugContext<'_>>,
|
||||
unwind_context: UnwindContext<'_>,
|
||||
unwind_context: UnwindContext,
|
||||
) -> ModuleCodegenResult {
|
||||
let mut product = module.finish();
|
||||
|
||||
|
@ -52,7 +47,7 @@ fn emit_module(
|
|||
tcx.sess.fatal(&format!("error writing object file: {}", err));
|
||||
}
|
||||
|
||||
let work_product = if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() {
|
||||
let work_product = if backend_config.disable_incr_cache {
|
||||
None
|
||||
} else {
|
||||
rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
|
||||
|
@ -110,23 +105,24 @@ fn module_codegen(
|
|||
let cgu = tcx.codegen_unit(cgu_name);
|
||||
let mono_items = cgu.items_in_deterministic_order(tcx);
|
||||
|
||||
let mut module = new_module(tcx, cgu_name.as_str().to_string());
|
||||
let isa = crate::build_isa(tcx.sess, &backend_config);
|
||||
let mut module = crate::backend::make_module(tcx.sess, isa, cgu_name.as_str().to_string());
|
||||
|
||||
let mut cx = crate::CodegenCx::new(
|
||||
tcx,
|
||||
backend_config,
|
||||
&mut module,
|
||||
backend_config.clone(),
|
||||
module.isa(),
|
||||
tcx.sess.opts.debuginfo != DebugInfo::None,
|
||||
);
|
||||
super::predefine_mono_items(&mut cx, &mono_items);
|
||||
super::predefine_mono_items(tcx, &mut module, &mono_items);
|
||||
for (mono_item, _) in mono_items {
|
||||
match mono_item {
|
||||
MonoItem::Fn(inst) => {
|
||||
cx.tcx.sess.time("codegen fn", || crate::base::codegen_fn(&mut cx, inst));
|
||||
}
|
||||
MonoItem::Static(def_id) => {
|
||||
crate::constant::codegen_static(&mut cx.constants_cx, def_id)
|
||||
cx.tcx
|
||||
.sess
|
||||
.time("codegen fn", || crate::base::codegen_fn(&mut cx, &mut module, inst));
|
||||
}
|
||||
MonoItem::Static(def_id) => crate::constant::codegen_static(tcx, &mut module, def_id),
|
||||
MonoItem::GlobalAsm(item_id) => {
|
||||
let item = cx.tcx.hir().item(item_id);
|
||||
if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind {
|
||||
|
@ -138,25 +134,28 @@ fn module_codegen(
|
|||
}
|
||||
}
|
||||
}
|
||||
let (global_asm, debug, mut unwind_context) =
|
||||
tcx.sess.time("finalize CodegenCx", || cx.finalize());
|
||||
crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, &mut unwind_context);
|
||||
crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, &mut cx.unwind_context, false);
|
||||
|
||||
let codegen_result = emit_module(
|
||||
tcx,
|
||||
cgu.name().as_str().to_string(),
|
||||
ModuleKind::Regular,
|
||||
module,
|
||||
debug,
|
||||
unwind_context,
|
||||
);
|
||||
let debug_context = cx.debug_context;
|
||||
let unwind_context = cx.unwind_context;
|
||||
let codegen_result = tcx.sess.time("write object file", || {
|
||||
emit_module(
|
||||
tcx,
|
||||
&backend_config,
|
||||
cgu.name().as_str().to_string(),
|
||||
ModuleKind::Regular,
|
||||
module,
|
||||
debug_context,
|
||||
unwind_context,
|
||||
)
|
||||
});
|
||||
|
||||
codegen_global_asm(tcx, &cgu.name().as_str(), &global_asm);
|
||||
codegen_global_asm(tcx, &cgu.name().as_str(), &cx.global_asm);
|
||||
|
||||
codegen_result
|
||||
}
|
||||
|
||||
pub(super) fn run_aot(
|
||||
pub(crate) fn run_aot(
|
||||
tcx: TyCtxt<'_>,
|
||||
backend_config: BackendConfig,
|
||||
metadata: EncodedMetadata,
|
||||
|
@ -193,14 +192,14 @@ pub(super) fn run_aot(
|
|||
}
|
||||
}
|
||||
|
||||
let modules = super::time(tcx, "codegen mono items", || {
|
||||
let modules = super::time(tcx, backend_config.display_cg_time, "codegen mono items", || {
|
||||
cgus.iter()
|
||||
.map(|cgu| {
|
||||
let cgu_reuse = determine_cgu_reuse(tcx, cgu);
|
||||
tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse);
|
||||
|
||||
match cgu_reuse {
|
||||
_ if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() => {}
|
||||
_ if backend_config.disable_incr_cache => {}
|
||||
CguReuse::No => {}
|
||||
CguReuse::PreLto => {
|
||||
return reuse_workproduct_for_cgu(tcx, &*cgu, &mut work_products);
|
||||
|
@ -212,7 +211,7 @@ pub(super) fn run_aot(
|
|||
let (ModuleCodegenResult(module, work_product), _) = tcx.dep_graph.with_task(
|
||||
dep_node,
|
||||
tcx,
|
||||
(backend_config, cgu.name()),
|
||||
(backend_config.clone(), cgu.name()),
|
||||
module_codegen,
|
||||
rustc_middle::dep_graph::hash_result,
|
||||
);
|
||||
|
@ -228,7 +227,10 @@ pub(super) fn run_aot(
|
|||
|
||||
tcx.sess.abort_if_errors();
|
||||
|
||||
let mut allocator_module = new_module(tcx, "allocator_shim".to_string());
|
||||
let isa = crate::build_isa(tcx.sess, &backend_config);
|
||||
let mut allocator_module =
|
||||
crate::backend::make_module(tcx.sess, isa, "allocator_shim".to_string());
|
||||
assert_eq!(pointer_ty(tcx), allocator_module.target_config().pointer_type());
|
||||
let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa(), true);
|
||||
let created_alloc_shim =
|
||||
crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context);
|
||||
|
@ -236,6 +238,7 @@ pub(super) fn run_aot(
|
|||
let allocator_module = if created_alloc_shim {
|
||||
let ModuleCodegenResult(module, work_product) = emit_module(
|
||||
tcx,
|
||||
&backend_config,
|
||||
"allocator_shim".to_string(),
|
||||
ModuleKind::Allocator,
|
||||
allocator_module,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! The JIT driver uses [`cranelift_simplejit`] to JIT execute programs without writing any object
|
||||
//! The JIT driver uses [`cranelift_jit`] to JIT execute programs without writing any object
|
||||
//! files.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
@ -8,32 +8,62 @@ use std::os::raw::{c_char, c_int};
|
|||
use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink};
|
||||
use rustc_codegen_ssa::CrateInfo;
|
||||
use rustc_middle::mir::mono::MonoItem;
|
||||
use rustc_session::config::EntryFnType;
|
||||
|
||||
use cranelift_jit::{JITBuilder, JITModule};
|
||||
|
||||
use crate::{prelude::*, BackendConfig};
|
||||
use crate::{CodegenCx, CodegenMode};
|
||||
|
||||
thread_local! {
|
||||
pub static BACKEND_CONFIG: RefCell<Option<BackendConfig>> = RefCell::new(None);
|
||||
pub static CURRENT_MODULE: RefCell<Option<JITModule>> = RefCell::new(None);
|
||||
struct JitState {
|
||||
backend_config: BackendConfig,
|
||||
jit_module: JITModule,
|
||||
}
|
||||
|
||||
pub(super) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
|
||||
if !tcx.sess.opts.output_types.should_codegen() {
|
||||
tcx.sess.fatal("JIT mode doesn't work with `cargo check`.");
|
||||
}
|
||||
thread_local! {
|
||||
static LAZY_JIT_STATE: RefCell<Option<JitState>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
fn create_jit_module<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
backend_config: &BackendConfig,
|
||||
hotswap: bool,
|
||||
) -> (JITModule, CodegenCx<'tcx>) {
|
||||
let imported_symbols = load_imported_symbols_for_jit(tcx);
|
||||
|
||||
let mut jit_builder =
|
||||
JITBuilder::with_isa(crate::build_isa(tcx.sess), cranelift_module::default_libcall_names());
|
||||
jit_builder.hotswap(matches!(backend_config.codegen_mode, CodegenMode::JitLazy));
|
||||
let isa = crate::build_isa(tcx.sess, backend_config);
|
||||
let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
|
||||
jit_builder.hotswap(hotswap);
|
||||
crate::compiler_builtins::register_functions_for_jit(&mut jit_builder);
|
||||
jit_builder.symbols(imported_symbols);
|
||||
let mut jit_module = JITModule::new(jit_builder);
|
||||
assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type());
|
||||
|
||||
let mut cx = crate::CodegenCx::new(tcx, backend_config.clone(), jit_module.isa(), false);
|
||||
|
||||
crate::allocator::codegen(tcx, &mut jit_module, &mut cx.unwind_context);
|
||||
crate::main_shim::maybe_create_entry_wrapper(
|
||||
tcx,
|
||||
&mut jit_module,
|
||||
&mut cx.unwind_context,
|
||||
true,
|
||||
);
|
||||
|
||||
(jit_module, cx)
|
||||
}
|
||||
|
||||
pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
|
||||
if !tcx.sess.opts.output_types.should_codegen() {
|
||||
tcx.sess.fatal("JIT mode doesn't work with `cargo check`");
|
||||
}
|
||||
|
||||
if !tcx.sess.crate_types().contains(&rustc_session::config::CrateType::Executable) {
|
||||
tcx.sess.fatal("can't jit non-executable crate");
|
||||
}
|
||||
|
||||
let (mut jit_module, mut cx) = create_jit_module(
|
||||
tcx,
|
||||
&backend_config,
|
||||
matches!(backend_config.codegen_mode, CodegenMode::JitLazy),
|
||||
);
|
||||
|
||||
let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
|
||||
let mono_items = cgus
|
||||
|
@ -44,52 +74,45 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
|
|||
.into_iter()
|
||||
.collect::<Vec<(_, (_, _))>>();
|
||||
|
||||
let mut cx = crate::CodegenCx::new(tcx, backend_config, &mut jit_module, false);
|
||||
|
||||
super::time(tcx, "codegen mono items", || {
|
||||
super::predefine_mono_items(&mut cx, &mono_items);
|
||||
super::time(tcx, backend_config.display_cg_time, "codegen mono items", || {
|
||||
super::predefine_mono_items(tcx, &mut jit_module, &mono_items);
|
||||
for (mono_item, _) in mono_items {
|
||||
match mono_item {
|
||||
MonoItem::Fn(inst) => match backend_config.codegen_mode {
|
||||
CodegenMode::Aot => unreachable!(),
|
||||
CodegenMode::Jit => {
|
||||
cx.tcx.sess.time("codegen fn", || crate::base::codegen_fn(&mut cx, inst));
|
||||
cx.tcx.sess.time("codegen fn", || {
|
||||
crate::base::codegen_fn(&mut cx, &mut jit_module, inst)
|
||||
});
|
||||
}
|
||||
CodegenMode::JitLazy => codegen_shim(&mut cx, inst),
|
||||
CodegenMode::JitLazy => codegen_shim(&mut cx, &mut jit_module, inst),
|
||||
},
|
||||
MonoItem::Static(def_id) => {
|
||||
crate::constant::codegen_static(&mut cx.constants_cx, def_id);
|
||||
crate::constant::codegen_static(tcx, &mut jit_module, def_id);
|
||||
}
|
||||
MonoItem::GlobalAsm(item_id) => {
|
||||
let item = cx.tcx.hir().item(item_id);
|
||||
let item = tcx.hir().item(item_id);
|
||||
tcx.sess.span_fatal(item.span, "Global asm is not supported in JIT mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let (global_asm, _debug, mut unwind_context) =
|
||||
tcx.sess.time("finalize CodegenCx", || cx.finalize());
|
||||
jit_module.finalize_definitions();
|
||||
|
||||
if !global_asm.is_empty() {
|
||||
if !cx.global_asm.is_empty() {
|
||||
tcx.sess.fatal("Inline asm is not supported in JIT mode");
|
||||
}
|
||||
|
||||
crate::allocator::codegen(tcx, &mut jit_module, &mut unwind_context);
|
||||
|
||||
tcx.sess.abort_if_errors();
|
||||
|
||||
jit_module.finalize_definitions();
|
||||
let _unwind_register_guard = unsafe { unwind_context.register_jit(&jit_module) };
|
||||
unsafe { cx.unwind_context.register_jit(&jit_module) };
|
||||
|
||||
println!(
|
||||
"Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed"
|
||||
);
|
||||
|
||||
let args = ::std::env::var("CG_CLIF_JIT_ARGS").unwrap_or_else(|_| String::new());
|
||||
let args = std::iter::once(&*tcx.crate_name(LOCAL_CRATE).as_str().to_string())
|
||||
.chain(args.split(' '))
|
||||
.chain(backend_config.jit_args.iter().map(|arg| &**arg))
|
||||
.map(|arg| CString::new(arg).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
|
||||
|
@ -98,8 +121,21 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
|
|||
// useful as some dynamic linkers use it as a marker to jump over.
|
||||
argv.push(std::ptr::null());
|
||||
|
||||
BACKEND_CONFIG.with(|tls_backend_config| {
|
||||
assert!(tls_backend_config.borrow_mut().replace(backend_config).is_none())
|
||||
let start_sig = Signature {
|
||||
params: vec![
|
||||
AbiParam::new(jit_module.target_config().pointer_type()),
|
||||
AbiParam::new(jit_module.target_config().pointer_type()),
|
||||
],
|
||||
returns: vec![AbiParam::new(jit_module.target_config().pointer_type() /*isize*/)],
|
||||
call_conv: CallConv::triple_default(&crate::target_triple(tcx.sess)),
|
||||
};
|
||||
let start_func_id = jit_module.declare_function("main", Linkage::Import, &start_sig).unwrap();
|
||||
let finalized_start: *const u8 = jit_module.get_finalized_function(start_func_id);
|
||||
|
||||
LAZY_JIT_STATE.with(|lazy_jit_state| {
|
||||
let mut lazy_jit_state = lazy_jit_state.borrow_mut();
|
||||
assert!(lazy_jit_state.is_none());
|
||||
*lazy_jit_state = Some(JitState { backend_config, jit_module });
|
||||
});
|
||||
|
||||
let (main_def_id, entry_ty) = tcx.entry_fn(LOCAL_CRATE).unwrap();
|
||||
|
@ -161,24 +197,23 @@ extern "C" fn __clif_jit_fn(instance_ptr: *const Instance<'static>) -> *const u8
|
|||
// lift is used to ensure the correct lifetime for instance.
|
||||
let instance = tcx.lift(unsafe { *instance_ptr }).unwrap();
|
||||
|
||||
CURRENT_MODULE.with(|jit_module| {
|
||||
let mut jit_module = jit_module.borrow_mut();
|
||||
let jit_module = jit_module.as_mut().unwrap();
|
||||
let backend_config =
|
||||
BACKEND_CONFIG.with(|backend_config| backend_config.borrow().clone().unwrap());
|
||||
LAZY_JIT_STATE.with(|lazy_jit_state| {
|
||||
let mut lazy_jit_state = lazy_jit_state.borrow_mut();
|
||||
let lazy_jit_state = lazy_jit_state.as_mut().unwrap();
|
||||
let jit_module = &mut lazy_jit_state.jit_module;
|
||||
let backend_config = lazy_jit_state.backend_config.clone();
|
||||
|
||||
let name = tcx.symbol_name(instance).name.to_string();
|
||||
let name = tcx.symbol_name(instance).name;
|
||||
let sig = crate::abi::get_function_sig(tcx, jit_module.isa().triple(), instance);
|
||||
let func_id = jit_module.declare_function(&name, Linkage::Export, &sig).unwrap();
|
||||
let func_id = jit_module.declare_function(name, Linkage::Export, &sig).unwrap();
|
||||
jit_module.prepare_for_function_redefine(func_id).unwrap();
|
||||
|
||||
let mut cx = crate::CodegenCx::new(tcx, backend_config, jit_module, false);
|
||||
tcx.sess.time("codegen fn", || crate::base::codegen_fn(&mut cx, instance));
|
||||
let mut cx = crate::CodegenCx::new(tcx, backend_config, jit_module.isa(), false);
|
||||
tcx.sess.time("codegen fn", || crate::base::codegen_fn(&mut cx, jit_module, instance));
|
||||
|
||||
let (global_asm, _debug_context, unwind_context) = cx.finalize();
|
||||
assert!(global_asm.is_empty());
|
||||
assert!(cx.global_asm.is_empty());
|
||||
jit_module.finalize_definitions();
|
||||
std::mem::forget(unsafe { unwind_context.register_jit(&jit_module) });
|
||||
unsafe { cx.unwind_context.register_jit(&jit_module) };
|
||||
jit_module.get_finalized_function(func_id)
|
||||
})
|
||||
})
|
||||
|
@ -248,35 +283,37 @@ fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> {
|
|||
imported_symbols
|
||||
}
|
||||
|
||||
fn codegen_shim<'tcx>(cx: &mut CodegenCx<'_, 'tcx>, inst: Instance<'tcx>) {
|
||||
fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx>, module: &mut JITModule, inst: Instance<'tcx>) {
|
||||
let tcx = cx.tcx;
|
||||
|
||||
let pointer_type = cx.module.target_config().pointer_type();
|
||||
let pointer_type = module.target_config().pointer_type();
|
||||
|
||||
let name = tcx.symbol_name(inst).name.to_string();
|
||||
let sig = crate::abi::get_function_sig(tcx, cx.module.isa().triple(), inst);
|
||||
let func_id = cx.module.declare_function(&name, Linkage::Export, &sig).unwrap();
|
||||
let name = tcx.symbol_name(inst).name;
|
||||
let sig = crate::abi::get_function_sig(tcx, module.isa().triple(), inst);
|
||||
let func_id = module.declare_function(name, Linkage::Export, &sig).unwrap();
|
||||
|
||||
let instance_ptr = Box::into_raw(Box::new(inst));
|
||||
|
||||
let jit_fn = cx
|
||||
.module
|
||||
let jit_fn = module
|
||||
.declare_function(
|
||||
"__clif_jit_fn",
|
||||
Linkage::Import,
|
||||
&Signature {
|
||||
call_conv: cx.module.target_config().default_call_conv,
|
||||
call_conv: module.target_config().default_call_conv,
|
||||
params: vec![AbiParam::new(pointer_type)],
|
||||
returns: vec![AbiParam::new(pointer_type)],
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut trampoline = Function::with_name_signature(ExternalName::default(), sig.clone());
|
||||
let mut builder_ctx = FunctionBuilderContext::new();
|
||||
let mut trampoline_builder = FunctionBuilder::new(&mut trampoline, &mut builder_ctx);
|
||||
cx.cached_context.clear();
|
||||
let trampoline = &mut cx.cached_context.func;
|
||||
trampoline.signature = sig.clone();
|
||||
|
||||
let jit_fn = cx.module.declare_func_in_func(jit_fn, trampoline_builder.func);
|
||||
let mut builder_ctx = FunctionBuilderContext::new();
|
||||
let mut trampoline_builder = FunctionBuilder::new(trampoline, &mut builder_ctx);
|
||||
|
||||
let jit_fn = module.declare_func_in_func(jit_fn, trampoline_builder.func);
|
||||
let sig_ref = trampoline_builder.func.import_signature(sig);
|
||||
|
||||
let entry_block = trampoline_builder.create_block();
|
||||
|
@ -291,10 +328,10 @@ fn codegen_shim<'tcx>(cx: &mut CodegenCx<'_, 'tcx>, inst: Instance<'tcx>) {
|
|||
let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec();
|
||||
trampoline_builder.ins().return_(&ret_vals);
|
||||
|
||||
cx.module
|
||||
module
|
||||
.define_function(
|
||||
func_id,
|
||||
&mut Context::for_function(trampoline),
|
||||
&mut cx.cached_context,
|
||||
&mut NullTrapSink {},
|
||||
&mut NullStackMapSink {},
|
||||
)
|
||||
|
|
|
@ -1,63 +1,37 @@
|
|||
//! Drivers are responsible for calling [`codegen_mono_item`] and performing any further actions
|
||||
//! like JIT executing or writing object files.
|
||||
//! Drivers are responsible for calling [`codegen_fn`] or [`codegen_static`] for each mono item and
|
||||
//! performing any further actions like JIT executing or writing object files.
|
||||
//!
|
||||
//! [`codegen_fn`]: crate::base::codegen_fn
|
||||
//! [`codegen_static`]: crate::constant::codegen_static
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
use rustc_middle::middle::cstore::EncodedMetadata;
|
||||
use rustc_middle::mir::mono::{Linkage as RLinkage, MonoItem, Visibility};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::CodegenMode;
|
||||
|
||||
mod aot;
|
||||
pub(crate) mod aot;
|
||||
#[cfg(feature = "jit")]
|
||||
mod jit;
|
||||
|
||||
pub(crate) fn codegen_crate(
|
||||
tcx: TyCtxt<'_>,
|
||||
metadata: EncodedMetadata,
|
||||
need_metadata_module: bool,
|
||||
backend_config: crate::BackendConfig,
|
||||
) -> Box<dyn Any> {
|
||||
tcx.sess.abort_if_errors();
|
||||
|
||||
match backend_config.codegen_mode {
|
||||
CodegenMode::Aot => aot::run_aot(tcx, backend_config, metadata, need_metadata_module),
|
||||
CodegenMode::Jit | CodegenMode::JitLazy => {
|
||||
let is_executable =
|
||||
tcx.sess.crate_types().contains(&rustc_session::config::CrateType::Executable);
|
||||
if !is_executable {
|
||||
tcx.sess.fatal("can't jit non-executable crate");
|
||||
}
|
||||
|
||||
#[cfg(feature = "jit")]
|
||||
let _: ! = jit::run_jit(tcx, backend_config);
|
||||
|
||||
#[cfg(not(feature = "jit"))]
|
||||
tcx.sess.fatal("jit support was disabled when compiling rustc_codegen_cranelift");
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) mod jit;
|
||||
|
||||
fn predefine_mono_items<'tcx>(
|
||||
cx: &mut crate::CodegenCx<'_, 'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
module: &mut dyn Module,
|
||||
mono_items: &[(MonoItem<'tcx>, (RLinkage, Visibility))],
|
||||
) {
|
||||
cx.tcx.sess.time("predefine functions", || {
|
||||
let is_compiler_builtins = cx.tcx.is_compiler_builtins(LOCAL_CRATE);
|
||||
tcx.sess.time("predefine functions", || {
|
||||
let is_compiler_builtins = tcx.is_compiler_builtins(LOCAL_CRATE);
|
||||
for &(mono_item, (linkage, visibility)) in mono_items {
|
||||
match mono_item {
|
||||
MonoItem::Fn(instance) => {
|
||||
let name = cx.tcx.symbol_name(instance).name.to_string();
|
||||
let name = tcx.symbol_name(instance).name;
|
||||
let _inst_guard = crate::PrintOnPanic(|| format!("{:?} {}", instance, name));
|
||||
let sig = get_function_sig(cx.tcx, cx.module.isa().triple(), instance);
|
||||
let sig = get_function_sig(tcx, module.isa().triple(), instance);
|
||||
let linkage = crate::linkage::get_clif_linkage(
|
||||
mono_item,
|
||||
linkage,
|
||||
visibility,
|
||||
is_compiler_builtins,
|
||||
);
|
||||
cx.module.declare_function(&name, linkage, &sig).unwrap();
|
||||
module.declare_function(name, linkage, &sig).unwrap();
|
||||
}
|
||||
MonoItem::Static(_) | MonoItem::GlobalAsm(_) => {}
|
||||
}
|
||||
|
@ -65,8 +39,8 @@ fn predefine_mono_items<'tcx>(
|
|||
});
|
||||
}
|
||||
|
||||
fn time<R>(tcx: TyCtxt<'_>, name: &'static str, f: impl FnOnce() -> R) -> R {
|
||||
if std::env::var("CG_CLIF_DISPLAY_CG_TIME").as_ref().map(|val| &**val) == Ok("1") {
|
||||
fn time<R>(tcx: TyCtxt<'_>, display: bool, name: &'static str, f: impl FnOnce() -> R) -> R {
|
||||
if display {
|
||||
println!("[{:<30}: {}] start", tcx.crate_name(LOCAL_CRATE), name);
|
||||
let before = std::time::Instant::now();
|
||||
let res = tcx.sess.time(name, f);
|
||||
|
|
|
@ -24,6 +24,64 @@ pub(crate) fn codegen_inline_asm<'tcx>(
|
|||
let true_ = fx.bcx.ins().iconst(types::I32, 1);
|
||||
fx.bcx.ins().trapnz(true_, TrapCode::User(1));
|
||||
return;
|
||||
} else if template[0] == InlineAsmTemplatePiece::String("mov rsi, rbx".to_string())
|
||||
&& template[1] == InlineAsmTemplatePiece::String("\n".to_string())
|
||||
&& template[2] == InlineAsmTemplatePiece::String("cpuid".to_string())
|
||||
&& template[3] == InlineAsmTemplatePiece::String("\n".to_string())
|
||||
&& template[4] == InlineAsmTemplatePiece::String("xchg rsi, rbx".to_string())
|
||||
{
|
||||
assert_eq!(operands.len(), 4);
|
||||
let (leaf, eax_place) = match operands[0] {
|
||||
InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
|
||||
let reg = expect_reg(reg);
|
||||
assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::ax));
|
||||
(
|
||||
crate::base::codegen_operand(fx, in_value).load_scalar(fx),
|
||||
crate::base::codegen_place(fx, out_place.unwrap()),
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let ebx_place = match operands[1] {
|
||||
InlineAsmOperand::Out { reg, late: true, place } => {
|
||||
let reg = expect_reg(reg);
|
||||
assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::si));
|
||||
crate::base::codegen_place(fx, place.unwrap())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let (sub_leaf, ecx_place) = match operands[2] {
|
||||
InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
|
||||
let reg = expect_reg(reg);
|
||||
assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::cx));
|
||||
(
|
||||
crate::base::codegen_operand(fx, in_value).load_scalar(fx),
|
||||
crate::base::codegen_place(fx, out_place.unwrap()),
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let edx_place = match operands[3] {
|
||||
InlineAsmOperand::Out { reg, late: true, place } => {
|
||||
let reg = expect_reg(reg);
|
||||
assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::dx));
|
||||
crate::base::codegen_place(fx, place.unwrap())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let (eax, ebx, ecx, edx) = crate::intrinsics::codegen_cpuid_call(fx, leaf, sub_leaf);
|
||||
|
||||
eax_place.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32)));
|
||||
ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32)));
|
||||
ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32)));
|
||||
edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32)));
|
||||
return;
|
||||
} else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") {
|
||||
// ___chkstk, ___chkstk_ms and __alloca are only used on Windows
|
||||
crate::trap::trap_unimplemented(fx, "Stack probes are not supported");
|
||||
} else if fx.tcx.symbol_name(fx.instance).name == "__alloca" {
|
||||
crate::trap::trap_unimplemented(fx, "Alloca is not supported");
|
||||
}
|
||||
|
||||
let mut slot_size = Size::from_bytes(0);
|
||||
|
@ -92,8 +150,7 @@ pub(crate) fn codegen_inline_asm<'tcx>(
|
|||
|
||||
let inline_asm_index = fx.inline_asm_index;
|
||||
fx.inline_asm_index += 1;
|
||||
let asm_name =
|
||||
format!("{}__inline_asm_{}", fx.tcx.symbol_name(fx.instance).name, inline_asm_index);
|
||||
let asm_name = format!("{}__inline_asm_{}", fx.symbol_name, inline_asm_index);
|
||||
|
||||
let generated_asm = generate_asm_wrapper(
|
||||
&asm_name,
|
||||
|
@ -202,7 +259,6 @@ fn call_inline_asm<'tcx>(
|
|||
}
|
||||
|
||||
let inline_asm_func = fx
|
||||
.cx
|
||||
.module
|
||||
.declare_function(
|
||||
asm_name,
|
||||
|
@ -214,7 +270,7 @@ fn call_inline_asm<'tcx>(
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
let inline_asm_func = fx.cx.module.declare_func_in_func(inline_asm_func, &mut fx.bcx.func);
|
||||
let inline_asm_func = fx.module.declare_func_in_func(inline_asm_func, &mut fx.bcx.func);
|
||||
if fx.clif_comments.enabled() {
|
||||
fx.add_comment(inline_asm_func, asm_name);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::prelude::*;
|
|||
pub(crate) fn codegen_cpuid_call<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
leaf: Value,
|
||||
_subleaf: Value,
|
||||
_sub_leaf: Value,
|
||||
) -> (Value, Value, Value, Value) {
|
||||
let leaf_0 = fx.bcx.create_block();
|
||||
let leaf_1 = fx.bcx.create_block();
|
||||
|
|
|
@ -22,7 +22,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
|
|||
};
|
||||
|
||||
// Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8`
|
||||
llvm.x86.sse2.pmovmskb.128 | llvm.x86.avx2.pmovmskb | llvm.x86.sse2.movmsk.pd, (c a) {
|
||||
"llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd", (c a) {
|
||||
let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx);
|
||||
let lane_ty = fx.clif_type(lane_ty).unwrap();
|
||||
assert!(lane_count <= 32);
|
||||
|
@ -51,7 +51,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
|
|||
let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.i32));
|
||||
ret.write_cvalue(fx, res);
|
||||
};
|
||||
llvm.x86.sse2.cmp.ps | llvm.x86.sse2.cmp.pd, (c x, c y, o kind) {
|
||||
"llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd", (c x, c y, o kind) {
|
||||
let kind_const = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const");
|
||||
let flt_cc = match kind_const.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind_const)) {
|
||||
0 => FloatCC::Equal,
|
||||
|
@ -81,7 +81,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
|
|||
bool_to_zero_or_max_uint(fx, res_lane_layout, res_lane)
|
||||
});
|
||||
};
|
||||
llvm.x86.sse2.psrli.d, (c a, o imm8) {
|
||||
"llvm.x86.sse2.psrli.d", (c a, o imm8) {
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const");
|
||||
simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| {
|
||||
let res_lane = match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
|
||||
|
@ -91,7 +91,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
|
|||
CValue::by_val(res_lane, res_lane_layout)
|
||||
});
|
||||
};
|
||||
llvm.x86.sse2.pslli.d, (c a, o imm8) {
|
||||
"llvm.x86.sse2.pslli.d", (c a, o imm8) {
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const");
|
||||
simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| {
|
||||
let res_lane = match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
|
||||
|
@ -101,7 +101,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
|
|||
CValue::by_val(res_lane, res_lane_layout)
|
||||
});
|
||||
};
|
||||
llvm.x86.sse2.storeu.dq, (v mem_addr, c a) {
|
||||
"llvm.x86.sse2.storeu.dq", (v mem_addr, c a) {
|
||||
// FIXME correctly handle the unalignment
|
||||
let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout());
|
||||
dest.write_cvalue(fx, a);
|
||||
|
|
|
@ -8,23 +8,25 @@ mod simd;
|
|||
pub(crate) use cpuid::codegen_cpuid_call;
|
||||
pub(crate) use llvm::codegen_llvm_intrinsic_call;
|
||||
|
||||
use rustc_span::symbol::{sym, kw};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
|
||||
use crate::prelude::*;
|
||||
use cranelift_codegen::ir::AtomicRmwOp;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
|
||||
macro intrinsic_pat {
|
||||
(_) => {
|
||||
_
|
||||
},
|
||||
($name:ident) => {
|
||||
stringify!($name)
|
||||
sym::$name
|
||||
},
|
||||
(kw.$name:ident) => {
|
||||
kw::$name
|
||||
},
|
||||
($name:literal) => {
|
||||
stringify!($name)
|
||||
$name
|
||||
},
|
||||
($x:ident . $($xs:tt).*) => {
|
||||
concat!(stringify!($x), ".", intrinsic_pat!($($xs).*))
|
||||
}
|
||||
}
|
||||
|
||||
macro intrinsic_arg {
|
||||
|
@ -87,7 +89,7 @@ macro call_intrinsic_match {
|
|||
)*) => {
|
||||
match $intrinsic {
|
||||
$(
|
||||
stringify!($name) => {
|
||||
sym::$name => {
|
||||
assert!($substs.is_noop());
|
||||
if let [$(ref $arg),*] = *$args {
|
||||
let ($($arg,)*) = (
|
||||
|
@ -400,18 +402,17 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let def_id = instance.def_id();
|
||||
let substs = instance.substs;
|
||||
|
||||
let intrinsic = fx.tcx.item_name(def_id).as_str();
|
||||
let intrinsic = &intrinsic[..];
|
||||
let intrinsic = fx.tcx.item_name(def_id);
|
||||
|
||||
let ret = match destination {
|
||||
Some((place, _)) => place,
|
||||
None => {
|
||||
// Insert non returning intrinsics here
|
||||
match intrinsic {
|
||||
"abort" => {
|
||||
sym::abort => {
|
||||
trap_abort(fx, "Called intrinsic::abort.");
|
||||
}
|
||||
"transmute" => {
|
||||
sym::transmute => {
|
||||
crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", span);
|
||||
}
|
||||
_ => unimplemented!("unsupported instrinsic {}", intrinsic),
|
||||
|
@ -420,7 +421,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
}
|
||||
};
|
||||
|
||||
if intrinsic.starts_with("simd_") {
|
||||
if intrinsic.as_str().starts_with("simd_") {
|
||||
self::simd::codegen_simd_intrinsic_call(fx, instance, args, ret, span);
|
||||
let ret_block = fx.get_block(destination.expect("SIMD intrinsics don't diverge").1);
|
||||
fx.bcx.ins().jump(ret_block, &[]);
|
||||
|
@ -470,8 +471,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
sinf64(flt) -> f64 => sin,
|
||||
cosf32(flt) -> f32 => cosf,
|
||||
cosf64(flt) -> f64 => cos,
|
||||
tanf32(flt) -> f32 => tanf,
|
||||
tanf64(flt) -> f64 => tan,
|
||||
}
|
||||
|
||||
intrinsic_match! {
|
||||
|
@ -496,12 +495,12 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
count
|
||||
};
|
||||
|
||||
if intrinsic.contains("nonoverlapping") {
|
||||
if intrinsic == sym::copy_nonoverlapping {
|
||||
// FIXME emit_small_memcpy
|
||||
fx.bcx.call_memcpy(fx.cx.module.target_config(), dst, src, byte_amount);
|
||||
fx.bcx.call_memcpy(fx.module.target_config(), dst, src, byte_amount);
|
||||
} else {
|
||||
// FIXME emit_small_memmove
|
||||
fx.bcx.call_memmove(fx.cx.module.target_config(), dst, src, byte_amount);
|
||||
fx.bcx.call_memmove(fx.module.target_config(), dst, src, byte_amount);
|
||||
}
|
||||
};
|
||||
// NOTE: the volatile variants have src and dst swapped
|
||||
|
@ -515,12 +514,12 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
};
|
||||
|
||||
// FIXME make the copy actually volatile when using emit_small_mem{cpy,move}
|
||||
if intrinsic.contains("nonoverlapping") {
|
||||
if intrinsic == sym::volatile_copy_nonoverlapping_memory {
|
||||
// FIXME emit_small_memcpy
|
||||
fx.bcx.call_memcpy(fx.cx.module.target_config(), dst, src, byte_amount);
|
||||
fx.bcx.call_memcpy(fx.module.target_config(), dst, src, byte_amount);
|
||||
} else {
|
||||
// FIXME emit_small_memmove
|
||||
fx.bcx.call_memmove(fx.cx.module.target_config(), dst, src, byte_amount);
|
||||
fx.bcx.call_memmove(fx.module.target_config(), dst, src, byte_amount);
|
||||
}
|
||||
};
|
||||
size_of_val, <T> (c ptr) {
|
||||
|
@ -552,27 +551,28 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
|
||||
};
|
||||
|
||||
_ if intrinsic.starts_with("unchecked_") || intrinsic == "exact_div", (c x, c y) {
|
||||
unchecked_add | unchecked_sub | unchecked_div | exact_div | unchecked_rem
|
||||
| unchecked_shl | unchecked_shr, (c x, c y) {
|
||||
// FIXME trap on overflow
|
||||
let bin_op = match intrinsic {
|
||||
"unchecked_add" => BinOp::Add,
|
||||
"unchecked_sub" => BinOp::Sub,
|
||||
"unchecked_div" | "exact_div" => BinOp::Div,
|
||||
"unchecked_rem" => BinOp::Rem,
|
||||
"unchecked_shl" => BinOp::Shl,
|
||||
"unchecked_shr" => BinOp::Shr,
|
||||
_ => unreachable!("intrinsic {}", intrinsic),
|
||||
sym::unchecked_add => BinOp::Add,
|
||||
sym::unchecked_sub => BinOp::Sub,
|
||||
sym::unchecked_div | sym::exact_div => BinOp::Div,
|
||||
sym::unchecked_rem => BinOp::Rem,
|
||||
sym::unchecked_shl => BinOp::Shl,
|
||||
sym::unchecked_shr => BinOp::Shr,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let res = crate::num::codegen_int_binop(fx, bin_op, x, y);
|
||||
ret.write_cvalue(fx, res);
|
||||
};
|
||||
_ if intrinsic.ends_with("_with_overflow"), (c x, c y) {
|
||||
add_with_overflow | sub_with_overflow | mul_with_overflow, (c x, c y) {
|
||||
assert_eq!(x.layout().ty, y.layout().ty);
|
||||
let bin_op = match intrinsic {
|
||||
"add_with_overflow" => BinOp::Add,
|
||||
"sub_with_overflow" => BinOp::Sub,
|
||||
"mul_with_overflow" => BinOp::Mul,
|
||||
_ => unreachable!("intrinsic {}", intrinsic),
|
||||
sym::add_with_overflow => BinOp::Add,
|
||||
sym::sub_with_overflow => BinOp::Sub,
|
||||
sym::mul_with_overflow => BinOp::Mul,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let res = crate::num::codegen_checked_int_binop(
|
||||
|
@ -583,12 +583,12 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
);
|
||||
ret.write_cvalue(fx, res);
|
||||
};
|
||||
_ if intrinsic.starts_with("saturating_"), <T> (c lhs, c rhs) {
|
||||
saturating_add | saturating_sub, <T> (c lhs, c rhs) {
|
||||
assert_eq!(lhs.layout().ty, rhs.layout().ty);
|
||||
let bin_op = match intrinsic {
|
||||
"saturating_add" => BinOp::Add,
|
||||
"saturating_sub" => BinOp::Sub,
|
||||
_ => unreachable!("intrinsic {}", intrinsic),
|
||||
sym::saturating_add => BinOp::Add,
|
||||
sym::saturating_sub => BinOp::Sub,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let signed = type_sign(T);
|
||||
|
@ -609,15 +609,15 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let (min, max) = type_min_max_value(&mut fx.bcx, clif_ty, signed);
|
||||
|
||||
let val = match (intrinsic, signed) {
|
||||
("saturating_add", false) => fx.bcx.ins().select(has_overflow, max, val),
|
||||
("saturating_sub", false) => fx.bcx.ins().select(has_overflow, min, val),
|
||||
("saturating_add", true) => {
|
||||
(sym::saturating_add, false) => fx.bcx.ins().select(has_overflow, max, val),
|
||||
(sym::saturating_sub, false) => fx.bcx.ins().select(has_overflow, min, val),
|
||||
(sym::saturating_add, true) => {
|
||||
let rhs = rhs.load_scalar(fx);
|
||||
let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
|
||||
let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min);
|
||||
fx.bcx.ins().select(has_overflow, sat_val, val)
|
||||
}
|
||||
("saturating_sub", true) => {
|
||||
(sym::saturating_sub, true) => {
|
||||
let rhs = rhs.load_scalar(fx);
|
||||
let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
|
||||
let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max);
|
||||
|
@ -632,11 +632,21 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
};
|
||||
rotate_left, <T>(v x, v y) {
|
||||
let layout = fx.layout_of(T);
|
||||
let y = if fx.bcx.func.dfg.value_type(y) == types::I128 {
|
||||
fx.bcx.ins().ireduce(types::I64, y)
|
||||
} else {
|
||||
y
|
||||
};
|
||||
let res = fx.bcx.ins().rotl(x, y);
|
||||
ret.write_cvalue(fx, CValue::by_val(res, layout));
|
||||
};
|
||||
rotate_right, <T>(v x, v y) {
|
||||
let layout = fx.layout_of(T);
|
||||
let y = if fx.bcx.func.dfg.value_type(y) == types::I128 {
|
||||
fx.bcx.ins().ireduce(types::I64, y)
|
||||
} else {
|
||||
y
|
||||
};
|
||||
let res = fx.bcx.ins().rotr(x, y);
|
||||
ret.write_cvalue(fx, CValue::by_val(res, layout));
|
||||
};
|
||||
|
@ -670,7 +680,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let dst_ptr = dst.load_scalar(fx);
|
||||
// FIXME make the memset actually volatile when switching to emit_small_memset
|
||||
// FIXME use emit_small_memset
|
||||
fx.bcx.call_memset(fx.cx.module.target_config(), dst_ptr, val, count);
|
||||
fx.bcx.call_memset(fx.module.target_config(), dst_ptr, val, count);
|
||||
};
|
||||
ctlz | ctlz_nonzero, <T> (v arg) {
|
||||
// FIXME trap on `ctlz_nonzero` with zero arg.
|
||||
|
@ -806,7 +816,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
if intrinsic == "assert_zero_valid" && !layout.might_permit_raw_init(fx, /*zero:*/ true).unwrap() {
|
||||
if intrinsic == sym::assert_zero_valid && !layout.might_permit_raw_init(fx, /*zero:*/ true).unwrap() {
|
||||
with_no_trimmed_paths(|| crate::base::codegen_panic(
|
||||
fx,
|
||||
&format!("attempted to zero-initialize type `{}`, which is invalid", T),
|
||||
|
@ -815,7 +825,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
if intrinsic == "assert_uninit_valid" && !layout.might_permit_raw_init(fx, /*zero:*/ false).unwrap() {
|
||||
if intrinsic == sym::assert_uninit_valid && !layout.might_permit_raw_init(fx, /*zero:*/ false).unwrap() {
|
||||
with_no_trimmed_paths(|| crate::base::codegen_panic(
|
||||
fx,
|
||||
&format!("attempted to leave type `{}` uninitialized, which is invalid", T),
|
||||
|
@ -827,7 +837,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
|
||||
volatile_load | unaligned_volatile_load, (c ptr) {
|
||||
// Cranelift treats loads as volatile by default
|
||||
// FIXME ignore during stack2reg optimization
|
||||
// FIXME correctly handle unaligned_volatile_load
|
||||
let inner_layout =
|
||||
fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty);
|
||||
|
@ -836,7 +845,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
};
|
||||
volatile_store | unaligned_volatile_store, (v ptr, c val) {
|
||||
// Cranelift treats stores as volatile by default
|
||||
// FIXME ignore during stack2reg optimization
|
||||
// FIXME correctly handle unaligned_volatile_store
|
||||
let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout());
|
||||
dest.write_cvalue(fx, val);
|
||||
|
@ -878,14 +886,14 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
ret.write_cvalue(fx, caller_location);
|
||||
};
|
||||
|
||||
_ if intrinsic.starts_with("atomic_fence"), () {
|
||||
_ if intrinsic.as_str().starts_with("atomic_fence"), () {
|
||||
fx.bcx.ins().fence();
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_singlethreadfence"), () {
|
||||
_ if intrinsic.as_str().starts_with("atomic_singlethreadfence"), () {
|
||||
// FIXME use a compiler fence once Cranelift supports it
|
||||
fx.bcx.ins().fence();
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_load"), <T> (v ptr) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_load"), <T> (v ptr) {
|
||||
validate_atomic_type!(fx, intrinsic, span, T);
|
||||
let ty = fx.clif_type(T).unwrap();
|
||||
|
||||
|
@ -894,14 +902,14 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let val = CValue::by_val(val, fx.layout_of(T));
|
||||
ret.write_cvalue(fx, val);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_store"), (v ptr, c val) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_store"), (v ptr, c val) {
|
||||
validate_atomic_type!(fx, intrinsic, span, val.layout().ty);
|
||||
|
||||
let val = val.load_scalar(fx);
|
||||
|
||||
fx.bcx.ins().atomic_store(MemFlags::trusted(), val, ptr);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_xchg"), (v ptr, c new) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_xchg"), (v ptr, c new) {
|
||||
let layout = new.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -913,7 +921,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_*
|
||||
_ if intrinsic.as_str().starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_*
|
||||
let layout = new.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
|
||||
|
@ -927,7 +935,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
ret.write_cvalue(fx, ret_val)
|
||||
};
|
||||
|
||||
_ if intrinsic.starts_with("atomic_xadd"), (v ptr, c amount) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_xadd"), (v ptr, c amount) {
|
||||
let layout = amount.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -939,7 +947,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_xsub"), (v ptr, c amount) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_xsub"), (v ptr, c amount) {
|
||||
let layout = amount.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -951,7 +959,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_and"), (v ptr, c src) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_and"), (v ptr, c src) {
|
||||
let layout = src.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -963,7 +971,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_or"), (v ptr, c src) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_or"), (v ptr, c src) {
|
||||
let layout = src.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -975,7 +983,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_xor"), (v ptr, c src) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_xor"), (v ptr, c src) {
|
||||
let layout = src.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -989,7 +997,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
};
|
||||
|
||||
// FIXME https://github.com/bytecodealliance/wasmtime/issues/2647
|
||||
_ if intrinsic.starts_with("atomic_nand"), (v ptr, c src) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_nand"), (v ptr, c src) {
|
||||
let layout = src.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -1001,7 +1009,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_max"), (v ptr, c src) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_max"), (v ptr, c src) {
|
||||
let layout = src.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -1013,7 +1021,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_umax"), (v ptr, c src) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_umax"), (v ptr, c src) {
|
||||
let layout = src.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -1025,7 +1033,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_min"), (v ptr, c src) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_min"), (v ptr, c src) {
|
||||
let layout = src.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -1037,7 +1045,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
};
|
||||
_ if intrinsic.starts_with("atomic_umin"), (v ptr, c src) {
|
||||
_ if intrinsic.as_str().starts_with("atomic_umin"), (v ptr, c src) {
|
||||
let layout = src.layout();
|
||||
validate_atomic_type!(fx, intrinsic, span, layout.ty);
|
||||
let ty = fx.clif_type(layout.ty).unwrap();
|
||||
|
@ -1071,7 +1079,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
ret.write_cvalue(fx, val);
|
||||
};
|
||||
|
||||
try, (v f, v data, v _catch_fn) {
|
||||
kw.Try, (v f, v data, v _catch_fn) {
|
||||
// FIXME once unwinding is supported, change this to actually catch panics
|
||||
let f_sig = fx.bcx.func.import_signature(Signature {
|
||||
call_conv: CallConv::triple_default(fx.triple()),
|
||||
|
@ -1088,11 +1096,11 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
|
|||
|
||||
fadd_fast | fsub_fast | fmul_fast | fdiv_fast | frem_fast, (c x, c y) {
|
||||
let res = crate::num::codegen_float_binop(fx, match intrinsic {
|
||||
"fadd_fast" => BinOp::Add,
|
||||
"fsub_fast" => BinOp::Sub,
|
||||
"fmul_fast" => BinOp::Mul,
|
||||
"fdiv_fast" => BinOp::Div,
|
||||
"frem_fast" => BinOp::Rem,
|
||||
sym::fadd_fast => BinOp::Add,
|
||||
sym::fsub_fast => BinOp::Sub,
|
||||
sym::fmul_fast => BinOp::Mul,
|
||||
sym::fdiv_fast => BinOp::Div,
|
||||
sym::frem_fast => BinOp::Rem,
|
||||
_ => unreachable!(),
|
||||
}, x, y);
|
||||
ret.write_cvalue(fx, res);
|
||||
|
|
|
@ -13,8 +13,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
|||
let def_id = instance.def_id();
|
||||
let substs = instance.substs;
|
||||
|
||||
let intrinsic = fx.tcx.item_name(def_id).as_str();
|
||||
let intrinsic = &intrinsic[..];
|
||||
let intrinsic = fx.tcx.item_name(def_id);
|
||||
|
||||
intrinsic_match! {
|
||||
fx, intrinsic, substs, args,
|
||||
|
@ -65,10 +64,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
|||
};
|
||||
|
||||
// simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
|
||||
_ if intrinsic.starts_with("simd_shuffle"), (c x, c y, o idx) {
|
||||
_ if intrinsic.as_str().starts_with("simd_shuffle"), (c x, c y, o idx) {
|
||||
validate_simd_type!(fx, intrinsic, span, x.layout().ty);
|
||||
|
||||
let n: u16 = intrinsic["simd_shuffle".len()..].parse().unwrap();
|
||||
let n: u16 = intrinsic.as_str()["simd_shuffle".len()..].parse().unwrap();
|
||||
|
||||
assert_eq!(x.layout(), y.layout());
|
||||
let layout = x.layout();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![feature(rustc_private, decl_macro, never_type, hash_drain_filter)]
|
||||
#![feature(rustc_private, decl_macro, never_type, hash_drain_filter, vec_into_raw_parts)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![warn(unused_lifetimes)]
|
||||
#![warn(unreachable_pub)]
|
||||
|
@ -23,7 +23,6 @@ extern crate rustc_target;
|
|||
extern crate rustc_driver;
|
||||
|
||||
use std::any::Any;
|
||||
use std::str::FromStr;
|
||||
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_codegen_ssa::CodegenResults;
|
||||
|
@ -34,9 +33,10 @@ use rustc_middle::ty::query::Providers;
|
|||
use rustc_session::config::OutputFilenames;
|
||||
use rustc_session::Session;
|
||||
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::settings::{self, Configurable};
|
||||
|
||||
use crate::constant::ConstantCx;
|
||||
pub use crate::config::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
mod abi;
|
||||
|
@ -49,6 +49,7 @@ mod cast;
|
|||
mod codegen_i128;
|
||||
mod common;
|
||||
mod compiler_builtins;
|
||||
mod config;
|
||||
mod constant;
|
||||
mod debuginfo;
|
||||
mod discriminant;
|
||||
|
@ -87,7 +88,6 @@ mod prelude {
|
|||
|
||||
pub(crate) use rustc_index::vec::Idx;
|
||||
|
||||
pub(crate) use cranelift_codegen::entity::EntitySet;
|
||||
pub(crate) use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
|
||||
pub(crate) use cranelift_codegen::ir::function::Function;
|
||||
pub(crate) use cranelift_codegen::ir::types;
|
||||
|
@ -119,95 +119,36 @@ impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
|
|||
}
|
||||
}
|
||||
|
||||
struct CodegenCx<'m, 'tcx: 'm> {
|
||||
/// The codegen context holds any information shared between the codegen of individual functions
|
||||
/// inside a single codegen unit with the exception of the Cranelift [`Module`](cranelift_module::Module).
|
||||
struct CodegenCx<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
module: &'m mut dyn Module,
|
||||
global_asm: String,
|
||||
constants_cx: ConstantCx,
|
||||
cached_context: Context,
|
||||
vtables: FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), DataId>,
|
||||
debug_context: Option<DebugContext<'tcx>>,
|
||||
unwind_context: UnwindContext<'tcx>,
|
||||
unwind_context: UnwindContext,
|
||||
}
|
||||
|
||||
impl<'m, 'tcx> CodegenCx<'m, 'tcx> {
|
||||
impl<'tcx> CodegenCx<'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
backend_config: BackendConfig,
|
||||
module: &'m mut dyn Module,
|
||||
isa: &dyn TargetIsa,
|
||||
debug_info: bool,
|
||||
) -> Self {
|
||||
let unwind_context = UnwindContext::new(
|
||||
tcx,
|
||||
module.isa(),
|
||||
matches!(backend_config.codegen_mode, CodegenMode::Aot),
|
||||
);
|
||||
let debug_context =
|
||||
if debug_info { Some(DebugContext::new(tcx, module.isa())) } else { None };
|
||||
assert_eq!(pointer_ty(tcx), isa.pointer_type());
|
||||
|
||||
let unwind_context =
|
||||
UnwindContext::new(tcx, isa, matches!(backend_config.codegen_mode, CodegenMode::Aot));
|
||||
let debug_context = if debug_info { Some(DebugContext::new(tcx, isa)) } else { None };
|
||||
CodegenCx {
|
||||
tcx,
|
||||
module,
|
||||
global_asm: String::new(),
|
||||
constants_cx: ConstantCx::default(),
|
||||
cached_context: Context::new(),
|
||||
vtables: FxHashMap::default(),
|
||||
debug_context,
|
||||
unwind_context,
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize(self) -> (String, Option<DebugContext<'tcx>>, UnwindContext<'tcx>) {
|
||||
self.constants_cx.finalize(self.tcx, self.module);
|
||||
(self.global_asm, self.debug_context, self.unwind_context)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CodegenMode {
|
||||
Aot,
|
||||
Jit,
|
||||
JitLazy,
|
||||
}
|
||||
|
||||
impl Default for CodegenMode {
|
||||
fn default() -> Self {
|
||||
CodegenMode::Aot
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CodegenMode {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"aot" => Ok(CodegenMode::Aot),
|
||||
"jit" => Ok(CodegenMode::Jit),
|
||||
"jit-lazy" => Ok(CodegenMode::JitLazy),
|
||||
_ => Err(format!("Unknown codegen mode `{}`", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct BackendConfig {
|
||||
pub codegen_mode: CodegenMode,
|
||||
}
|
||||
|
||||
impl BackendConfig {
|
||||
fn from_opts(opts: &[String]) -> Result<Self, String> {
|
||||
let mut config = BackendConfig::default();
|
||||
for opt in opts {
|
||||
if let Some((name, value)) = opt.split_once('=') {
|
||||
match name {
|
||||
"mode" => config.codegen_mode = value.parse()?,
|
||||
_ => return Err(format!("Unknown option `{}`", name)),
|
||||
}
|
||||
} else {
|
||||
return Err(format!("Invalid option `{}`", opt));
|
||||
}
|
||||
}
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CraneliftCodegenBackend {
|
||||
|
@ -240,13 +181,23 @@ impl CodegenBackend for CraneliftCodegenBackend {
|
|||
metadata: EncodedMetadata,
|
||||
need_metadata_module: bool,
|
||||
) -> Box<dyn Any> {
|
||||
let config = if let Some(config) = self.config {
|
||||
tcx.sess.abort_if_errors();
|
||||
let config = if let Some(config) = self.config.clone() {
|
||||
config
|
||||
} else {
|
||||
BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args)
|
||||
.unwrap_or_else(|err| tcx.sess.fatal(&err))
|
||||
};
|
||||
driver::codegen_crate(tcx, metadata, need_metadata_module, config)
|
||||
match config.codegen_mode {
|
||||
CodegenMode::Aot => driver::aot::run_aot(tcx, config, metadata, need_metadata_module),
|
||||
CodegenMode::Jit | CodegenMode::JitLazy => {
|
||||
#[cfg(feature = "jit")]
|
||||
let _: ! = driver::jit::run_jit(tcx, config);
|
||||
|
||||
#[cfg(not(feature = "jit"))]
|
||||
tcx.sess.fatal("jit support was disabled when compiling rustc_codegen_cranelift");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn join_codegen(
|
||||
|
@ -284,7 +235,7 @@ fn target_triple(sess: &Session) -> target_lexicon::Triple {
|
|||
sess.target.llvm_target.parse().unwrap()
|
||||
}
|
||||
|
||||
fn build_isa(sess: &Session) -> Box<dyn isa::TargetIsa + 'static> {
|
||||
fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box<dyn isa::TargetIsa + 'static> {
|
||||
use target_lexicon::BinaryFormat;
|
||||
|
||||
let target_triple = crate::target_triple(sess);
|
||||
|
@ -292,9 +243,8 @@ fn build_isa(sess: &Session) -> Box<dyn isa::TargetIsa + 'static> {
|
|||
let mut flags_builder = settings::builder();
|
||||
flags_builder.enable("is_pic").unwrap();
|
||||
flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided
|
||||
let enable_verifier =
|
||||
cfg!(debug_assertions) || std::env::var("CG_CLIF_ENABLE_VERIFIER").is_ok();
|
||||
flags_builder.set("enable_verifier", if enable_verifier { "true" } else { "false" }).unwrap();
|
||||
let enable_verifier = if backend_config.enable_verifier { "true" } else { "false" };
|
||||
flags_builder.set("enable_verifier", enable_verifier).unwrap();
|
||||
|
||||
let tls_model = match target_triple.binary_format {
|
||||
BinaryFormat::Elf => "elf_gd",
|
||||
|
@ -322,10 +272,28 @@ fn build_isa(sess: &Session) -> Box<dyn isa::TargetIsa + 'static> {
|
|||
let flags = settings::Flags::new(flags_builder);
|
||||
|
||||
let variant = cranelift_codegen::isa::BackendVariant::MachInst;
|
||||
let mut isa_builder = cranelift_codegen::isa::lookup_variant(target_triple, variant).unwrap();
|
||||
// Don't use "haswell", as it implies `has_lzcnt`.macOS CI is still at Ivy Bridge EP, so `lzcnt`
|
||||
// is interpreted as `bsr`.
|
||||
isa_builder.enable("nehalem").unwrap();
|
||||
|
||||
let isa_builder = match sess.opts.cg.target_cpu.as_deref() {
|
||||
Some("native") => {
|
||||
let builder = cranelift_native::builder_with_options(variant, true).unwrap();
|
||||
builder
|
||||
}
|
||||
Some(value) => {
|
||||
let mut builder = cranelift_codegen::isa::lookup_variant(target_triple, variant).unwrap();
|
||||
if let Err(_) = builder.enable(value) {
|
||||
sess.fatal("The specified target cpu isn't currently supported by Cranelift.");
|
||||
}
|
||||
builder
|
||||
}
|
||||
None => {
|
||||
let mut builder = cranelift_codegen::isa::lookup_variant(target_triple, variant).unwrap();
|
||||
// Don't use "haswell" as the default, as it implies `has_lzcnt`.
|
||||
// macOS CI is still at Ivy Bridge EP, so `lzcnt` is interpreted as `bsr`.
|
||||
builder.enable("nehalem").unwrap();
|
||||
builder
|
||||
}
|
||||
};
|
||||
|
||||
isa_builder.finish(flags)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ pub(crate) fn get_clif_linkage(
|
|||
(RLinkage::External, Visibility::Default) => Linkage::Export,
|
||||
(RLinkage::Internal, Visibility::Default) => Linkage::Local,
|
||||
(RLinkage::External, Visibility::Hidden) => Linkage::Hidden,
|
||||
(RLinkage::WeakAny, Visibility::Default) => Linkage::Preemptible,
|
||||
_ => panic!("{:?} = {:?} {:?}", mono_item, linkage, visibility),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink};
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::ty::subst::GenericArg;
|
||||
use rustc_middle::ty::AssocKind;
|
||||
use rustc_session::config::EntryFnType;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
|
@ -9,9 +12,10 @@ use crate::prelude::*;
|
|||
pub(crate) fn maybe_create_entry_wrapper(
|
||||
tcx: TyCtxt<'_>,
|
||||
module: &mut impl Module,
|
||||
unwind_context: &mut UnwindContext<'_>,
|
||||
unwind_context: &mut UnwindContext,
|
||||
is_jit: bool,
|
||||
) {
|
||||
let (main_def_id, use_start_lang_item) = match tcx.entry_fn(LOCAL_CRATE) {
|
||||
let (main_def_id, is_main_fn) = match tcx.entry_fn(LOCAL_CRATE) {
|
||||
Some((def_id, entry_ty)) => (
|
||||
def_id,
|
||||
match entry_ty {
|
||||
|
@ -23,18 +27,19 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||
};
|
||||
|
||||
let instance = Instance::mono(tcx, main_def_id).polymorphize(tcx);
|
||||
if module.get_name(&*tcx.symbol_name(instance).name).is_none() {
|
||||
if !is_jit && module.get_name(&*tcx.symbol_name(instance).name).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
create_entry_fn(tcx, module, unwind_context, main_def_id, use_start_lang_item);
|
||||
create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn);
|
||||
|
||||
fn create_entry_fn(
|
||||
tcx: TyCtxt<'_>,
|
||||
m: &mut impl Module,
|
||||
unwind_context: &mut UnwindContext<'_>,
|
||||
unwind_context: &mut UnwindContext,
|
||||
rust_main_def_id: DefId,
|
||||
use_start_lang_item: bool,
|
||||
ignore_lang_start_wrapper: bool,
|
||||
is_main_fn: bool,
|
||||
) {
|
||||
let main_ret_ty = tcx.fn_sig(rust_main_def_id).output();
|
||||
// Given that `main()` has no arguments,
|
||||
|
@ -57,9 +62,9 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||
|
||||
let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx);
|
||||
|
||||
let main_name = tcx.symbol_name(instance).name.to_string();
|
||||
let main_name = tcx.symbol_name(instance).name;
|
||||
let main_sig = get_function_sig(tcx, m.isa().triple(), instance);
|
||||
let main_func_id = m.declare_function(&main_name, Linkage::Import, &main_sig).unwrap();
|
||||
let main_func_id = m.declare_function(main_name, Linkage::Import, &main_sig).unwrap();
|
||||
|
||||
let mut ctx = Context::new();
|
||||
ctx.func = Function::with_name_signature(ExternalName::user(0, 0), cmain_sig);
|
||||
|
@ -74,7 +79,47 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||
|
||||
let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func);
|
||||
|
||||
let call_inst = if use_start_lang_item {
|
||||
let result = if is_main_fn && ignore_lang_start_wrapper {
|
||||
// regular main fn, but ignoring #[lang = "start"] as we are running in the jit
|
||||
// FIXME set program arguments somehow
|
||||
let call_inst = bcx.ins().call(main_func_ref, &[]);
|
||||
let call_results = bcx.func.dfg.inst_results(call_inst).to_owned();
|
||||
|
||||
let termination_trait = tcx.require_lang_item(LangItem::Termination, None);
|
||||
let report = tcx
|
||||
.associated_items(termination_trait)
|
||||
.find_by_name_and_kind(
|
||||
tcx,
|
||||
Ident::from_str("report"),
|
||||
AssocKind::Fn,
|
||||
termination_trait,
|
||||
)
|
||||
.unwrap();
|
||||
let report = Instance::resolve(
|
||||
tcx,
|
||||
ParamEnv::reveal_all(),
|
||||
report.def_id,
|
||||
tcx.mk_substs([GenericArg::from(main_ret_ty)].iter()),
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let report_name = tcx.symbol_name(report).name;
|
||||
let report_sig = get_function_sig(tcx, m.isa().triple(), report);
|
||||
let report_func_id =
|
||||
m.declare_function(report_name, Linkage::Import, &report_sig).unwrap();
|
||||
let report_func_ref = m.declare_func_in_func(report_func_id, &mut bcx.func);
|
||||
|
||||
// FIXME do proper abi handling instead of expecting the pass mode to be identical
|
||||
// for returns and arguments.
|
||||
let report_call_inst = bcx.ins().call(report_func_ref, &call_results);
|
||||
let res = bcx.func.dfg.inst_results(report_call_inst)[0];
|
||||
match m.target_config().pointer_type() {
|
||||
types::I32 => res,
|
||||
types::I64 => bcx.ins().sextend(types::I64, res),
|
||||
_ => unimplemented!("16bit systems are not yet supported"),
|
||||
}
|
||||
} else if is_main_fn {
|
||||
let start_def_id = tcx.require_lang_item(LangItem::Start, None);
|
||||
let start_instance = Instance::resolve(
|
||||
tcx,
|
||||
|
@ -90,13 +135,14 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||
let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref);
|
||||
|
||||
let func_ref = m.declare_func_in_func(start_func_id, &mut bcx.func);
|
||||
bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv])
|
||||
let call_inst = bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv]);
|
||||
bcx.inst_results(call_inst)[0]
|
||||
} else {
|
||||
// using user-defined start fn
|
||||
bcx.ins().call(main_func_ref, &[arg_argc, arg_argv])
|
||||
let call_inst = bcx.ins().call(main_func_ref, &[arg_argc, arg_argv]);
|
||||
bcx.inst_results(call_inst)[0]
|
||||
};
|
||||
|
||||
let result = bcx.inst_results(call_inst)[0];
|
||||
bcx.ins().return_(&[result]);
|
||||
bcx.seal_all_blocks();
|
||||
bcx.finalize();
|
||||
|
|
|
@ -8,13 +8,24 @@ use rustc_data_structures::memmap::Mmap;
|
|||
use rustc_data_structures::owning_ref::OwningRef;
|
||||
use rustc_data_structures::rustc_erase_owner;
|
||||
use rustc_data_structures::sync::MetadataRef;
|
||||
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader};
|
||||
use rustc_middle::middle::cstore::MetadataLoader;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config;
|
||||
use rustc_target::spec::Target;
|
||||
|
||||
use crate::backend::WriteMetadata;
|
||||
|
||||
/// The metadata loader used by cg_clif.
|
||||
///
|
||||
/// The metadata is stored in the same format as cg_llvm.
|
||||
///
|
||||
/// # Metadata location
|
||||
///
|
||||
/// <dl>
|
||||
/// <dt>rlib</dt>
|
||||
/// <dd>The metadata can be found in the `lib.rmeta` file inside of the ar archive.</dd>
|
||||
/// <dt>dylib</dt>
|
||||
/// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd>
|
||||
/// </dl>
|
||||
pub(crate) struct CraneliftMetadataLoader;
|
||||
|
||||
fn load_metadata_with(
|
||||
|
@ -58,54 +69,16 @@ impl MetadataLoader for CraneliftMetadataLoader {
|
|||
}
|
||||
|
||||
// Adapted from https://github.com/rust-lang/rust/blob/da573206f87b5510de4b0ee1a9c044127e409bd3/src/librustc_codegen_llvm/base.rs#L47-L112
|
||||
pub(crate) fn write_metadata<P: WriteMetadata>(
|
||||
tcx: TyCtxt<'_>,
|
||||
product: &mut P,
|
||||
) -> EncodedMetadata {
|
||||
pub(crate) fn write_metadata<O: WriteMetadata>(tcx: TyCtxt<'_>, object: &mut O) {
|
||||
use snap::write::FrameEncoder;
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum MetadataKind {
|
||||
None,
|
||||
Uncompressed,
|
||||
Compressed,
|
||||
}
|
||||
|
||||
let kind = tcx
|
||||
.sess
|
||||
.crate_types()
|
||||
.iter()
|
||||
.map(|ty| match *ty {
|
||||
config::CrateType::Executable
|
||||
| config::CrateType::Staticlib
|
||||
| config::CrateType::Cdylib => MetadataKind::None,
|
||||
|
||||
config::CrateType::Rlib => MetadataKind::Uncompressed,
|
||||
|
||||
config::CrateType::Dylib | config::CrateType::ProcMacro => MetadataKind::Compressed,
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(MetadataKind::None);
|
||||
|
||||
if kind == MetadataKind::None {
|
||||
return EncodedMetadata::new();
|
||||
}
|
||||
|
||||
let metadata = tcx.encode_metadata();
|
||||
if kind == MetadataKind::Uncompressed {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
assert!(kind == MetadataKind::Compressed);
|
||||
let mut compressed = tcx.metadata_encoding_version();
|
||||
FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap();
|
||||
|
||||
product.add_rustc_section(
|
||||
object.add_rustc_section(
|
||||
rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx),
|
||||
compressed,
|
||||
tcx.sess.target.is_like_osx,
|
||||
);
|
||||
|
||||
metadata
|
||||
}
|
||||
|
|
|
@ -271,14 +271,17 @@ pub(crate) fn codegen_checked_int_binop<'tcx>(
|
|||
let val_hi = fx.bcx.ins().umulhi(lhs, rhs);
|
||||
fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0)
|
||||
} else {
|
||||
// Based on LLVM's instruction sequence for compiling
|
||||
// a.checked_mul(b).is_some() to riscv64gc:
|
||||
// mulh a2, a0, a1
|
||||
// mul a0, a0, a1
|
||||
// srai a0, a0, 63
|
||||
// xor a0, a0, a2
|
||||
// snez a0, a0
|
||||
let val_hi = fx.bcx.ins().smulhi(lhs, rhs);
|
||||
let not_all_zero = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0);
|
||||
let not_all_ones = fx.bcx.ins().icmp_imm(
|
||||
IntCC::NotEqual,
|
||||
val_hi,
|
||||
u64::try_from((1u128 << ty.bits()) - 1).unwrap() as i64,
|
||||
);
|
||||
fx.bcx.ins().band(not_all_zero, not_all_ones)
|
||||
let val_sign = fx.bcx.ins().sshr_imm(val, i64::from(ty.bits() - 1));
|
||||
let xor = fx.bcx.ins().bxor(val_hi, val_sign);
|
||||
fx.bcx.ins().icmp_imm(IntCC::NotEqual, xor, 0)
|
||||
};
|
||||
(val, has_overflow)
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
//! This optimization moves cold code to the end of the function.
|
||||
//!
|
||||
//! Some code is executed much less often than other code. For example panicking or the
|
||||
//! landingpads for unwinding. By moving this cold code to the end of the function the average
|
||||
//! amount of jumps is reduced and the code locality is improved.
|
||||
//!
|
||||
//! # Undefined behaviour
|
||||
//!
|
||||
//! This optimization doesn't assume anything that isn't already assumed by Cranelift itself.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub(super) fn optimize_function(ctx: &mut Context, cold_blocks: &EntitySet<Block>) {
|
||||
// FIXME Move the block in place instead of remove and append once
|
||||
// bytecodealliance/cranelift#1339 is implemented.
|
||||
|
||||
let mut block_insts = FxHashMap::default();
|
||||
for block in cold_blocks.keys().filter(|&block| cold_blocks.contains(block)) {
|
||||
let insts = ctx.func.layout.block_insts(block).collect::<Vec<_>>();
|
||||
for &inst in &insts {
|
||||
ctx.func.layout.remove_inst(inst);
|
||||
}
|
||||
block_insts.insert(block, insts);
|
||||
ctx.func.layout.remove_block(block);
|
||||
}
|
||||
|
||||
// And then append them at the back again.
|
||||
for block in cold_blocks.keys().filter(|&block| cold_blocks.contains(block)) {
|
||||
ctx.func.layout.append_block(block);
|
||||
for inst in block_insts.remove(&block).unwrap() {
|
||||
ctx.func.layout.append_inst(inst, block);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,29 +2,16 @@
|
|||
|
||||
use crate::prelude::*;
|
||||
|
||||
mod code_layout;
|
||||
pub(crate) mod peephole;
|
||||
mod stack2reg;
|
||||
|
||||
pub(crate) fn optimize_function<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
ctx: &mut Context,
|
||||
cold_blocks: &EntitySet<Block>,
|
||||
clif_comments: &mut crate::pretty_clif::CommentWriter,
|
||||
) {
|
||||
// The code_layout optimization is very cheap.
|
||||
self::code_layout::optimize_function(ctx, cold_blocks);
|
||||
// FIXME classify optimizations over opt levels once we have more
|
||||
|
||||
if tcx.sess.opts.optimize == rustc_session::config::OptLevel::No {
|
||||
return; // FIXME classify optimizations over opt levels
|
||||
}
|
||||
|
||||
// FIXME(#1142) stack2reg miscompiles lewton
|
||||
if false {
|
||||
self::stack2reg::optimize_function(ctx, clif_comments);
|
||||
}
|
||||
|
||||
crate::pretty_clif::write_clif_file(tcx, "stack2reg", None, instance, &ctx, &*clif_comments);
|
||||
crate::pretty_clif::write_clif_file(tcx, "preopt", None, instance, &ctx, &*clif_comments);
|
||||
crate::base::verify_func(tcx, &*clif_comments, &ctx.func);
|
||||
}
|
||||
|
|
|
@ -1,486 +0,0 @@
|
|||
//! This optimization replaces stack accesses with SSA variables and removes dead stores when possible.
|
||||
//!
|
||||
//! # Undefined behaviour
|
||||
//!
|
||||
//! This optimization is based on the assumption that stack slots which don't have their address
|
||||
//! leaked through `stack_addr` are only accessed using `stack_load` and `stack_store` in the
|
||||
//! function which has the stack slots. This optimization also assumes that stack slot accesses
|
||||
//! are never out of bounds. If these assumptions are not correct, then this optimization may remove
|
||||
//! `stack_store` instruction incorrectly, or incorrectly use a previously stored value as the value
|
||||
//! being loaded by a `stack_load`.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::ops::Not;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
||||
use cranelift_codegen::cursor::{Cursor, FuncCursor};
|
||||
use cranelift_codegen::ir::immediates::Offset32;
|
||||
use cranelift_codegen::ir::{InstructionData, Opcode, ValueDef};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Workaround for `StackSlot` not implementing `Ord`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
struct OrdStackSlot(StackSlot);
|
||||
|
||||
impl fmt::Debug for OrdStackSlot {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for OrdStackSlot {
|
||||
fn partial_cmp(&self, rhs: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.0.as_u32().partial_cmp(&rhs.0.as_u32())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for OrdStackSlot {
|
||||
fn cmp(&self, rhs: &Self) -> std::cmp::Ordering {
|
||||
self.0.as_u32().cmp(&rhs.0.as_u32())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct StackSlotUsage {
|
||||
stack_addr: FxHashSet<Inst>,
|
||||
stack_load: FxHashSet<Inst>,
|
||||
stack_store: FxHashSet<Inst>,
|
||||
}
|
||||
|
||||
impl StackSlotUsage {
|
||||
fn potential_stores_for_load(&self, ctx: &Context, load: Inst) -> Vec<Inst> {
|
||||
self.stack_store
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|&store| {
|
||||
match spatial_overlap(&ctx.func, store, load) {
|
||||
SpatialOverlap::No => false, // Can never be the source of the loaded value.
|
||||
SpatialOverlap::Partial | SpatialOverlap::Full => true,
|
||||
}
|
||||
})
|
||||
.filter(|&store| {
|
||||
match temporal_order(ctx, store, load) {
|
||||
TemporalOrder::NeverBefore => false, // Can never be the source of the loaded value.
|
||||
TemporalOrder::MaybeBefore | TemporalOrder::DefinitivelyBefore => true,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Inst>>()
|
||||
}
|
||||
|
||||
fn potential_loads_of_store(&self, ctx: &Context, store: Inst) -> Vec<Inst> {
|
||||
self.stack_load
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|&load| {
|
||||
match spatial_overlap(&ctx.func, store, load) {
|
||||
SpatialOverlap::No => false, // Can never be the source of the loaded value.
|
||||
SpatialOverlap::Partial | SpatialOverlap::Full => true,
|
||||
}
|
||||
})
|
||||
.filter(|&load| {
|
||||
match temporal_order(ctx, store, load) {
|
||||
TemporalOrder::NeverBefore => false, // Can never be the source of the loaded value.
|
||||
TemporalOrder::MaybeBefore | TemporalOrder::DefinitivelyBefore => true,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Inst>>()
|
||||
}
|
||||
|
||||
fn remove_unused_stack_addr(func: &mut Function, inst: Inst) {
|
||||
func.dfg.detach_results(inst);
|
||||
func.dfg.replace(inst).nop();
|
||||
}
|
||||
|
||||
fn remove_unused_load(func: &mut Function, load: Inst) {
|
||||
func.dfg.detach_results(load);
|
||||
func.dfg.replace(load).nop();
|
||||
}
|
||||
|
||||
fn remove_dead_store(&mut self, func: &mut Function, store: Inst) {
|
||||
func.dfg.replace(store).nop();
|
||||
self.stack_store.remove(&store);
|
||||
}
|
||||
|
||||
fn change_load_to_alias(&mut self, func: &mut Function, load: Inst, value: Value) {
|
||||
let loaded_value = func.dfg.inst_results(load)[0];
|
||||
let loaded_type = func.dfg.value_type(loaded_value);
|
||||
|
||||
if func.dfg.value_type(value) == loaded_type {
|
||||
func.dfg.detach_results(load);
|
||||
func.dfg.replace(load).nop();
|
||||
func.dfg.change_to_alias(loaded_value, value);
|
||||
} else {
|
||||
func.dfg.replace(load).bitcast(loaded_type, value);
|
||||
}
|
||||
|
||||
self.stack_load.remove(&load);
|
||||
}
|
||||
}
|
||||
|
||||
struct OptimizeContext<'a> {
|
||||
ctx: &'a mut Context,
|
||||
stack_slot_usage_map: BTreeMap<OrdStackSlot, StackSlotUsage>,
|
||||
}
|
||||
|
||||
impl<'a> OptimizeContext<'a> {
|
||||
fn for_context(ctx: &'a mut Context) -> Self {
|
||||
ctx.flowgraph(); // Compute cfg and domtree.
|
||||
|
||||
// Record all stack_addr, stack_load and stack_store instructions.
|
||||
let mut stack_slot_usage_map = BTreeMap::<OrdStackSlot, StackSlotUsage>::new();
|
||||
|
||||
let mut cursor = FuncCursor::new(&mut ctx.func);
|
||||
while let Some(_block) = cursor.next_block() {
|
||||
while let Some(inst) = cursor.next_inst() {
|
||||
match cursor.func.dfg[inst] {
|
||||
InstructionData::StackLoad {
|
||||
opcode: Opcode::StackAddr,
|
||||
stack_slot,
|
||||
offset: _,
|
||||
} => {
|
||||
stack_slot_usage_map
|
||||
.entry(OrdStackSlot(stack_slot))
|
||||
.or_insert_with(StackSlotUsage::default)
|
||||
.stack_addr
|
||||
.insert(inst);
|
||||
}
|
||||
InstructionData::StackLoad {
|
||||
opcode: Opcode::StackLoad,
|
||||
stack_slot,
|
||||
offset: _,
|
||||
} => {
|
||||
stack_slot_usage_map
|
||||
.entry(OrdStackSlot(stack_slot))
|
||||
.or_insert_with(StackSlotUsage::default)
|
||||
.stack_load
|
||||
.insert(inst);
|
||||
}
|
||||
InstructionData::StackStore {
|
||||
opcode: Opcode::StackStore,
|
||||
arg: _,
|
||||
stack_slot,
|
||||
offset: _,
|
||||
} => {
|
||||
stack_slot_usage_map
|
||||
.entry(OrdStackSlot(stack_slot))
|
||||
.or_insert_with(StackSlotUsage::default)
|
||||
.stack_store
|
||||
.insert(inst);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OptimizeContext { ctx, stack_slot_usage_map }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn optimize_function(
|
||||
ctx: &mut Context,
|
||||
clif_comments: &mut crate::pretty_clif::CommentWriter,
|
||||
) {
|
||||
combine_stack_addr_with_load_store(&mut ctx.func);
|
||||
|
||||
let mut opt_ctx = OptimizeContext::for_context(ctx);
|
||||
|
||||
// FIXME Repeat following instructions until fixpoint.
|
||||
|
||||
remove_unused_stack_addr_and_stack_load(&mut opt_ctx);
|
||||
|
||||
if clif_comments.enabled() {
|
||||
for (&OrdStackSlot(stack_slot), usage) in &opt_ctx.stack_slot_usage_map {
|
||||
clif_comments.add_comment(stack_slot, format!("used by: {:?}", usage));
|
||||
}
|
||||
}
|
||||
|
||||
for (stack_slot, users) in opt_ctx.stack_slot_usage_map.iter_mut() {
|
||||
if users.stack_addr.is_empty().not() {
|
||||
// Stack addr leaked; there may be unknown loads and stores.
|
||||
// FIXME use stacked borrows to optimize
|
||||
continue;
|
||||
}
|
||||
|
||||
for load in users.stack_load.clone().into_iter() {
|
||||
let potential_stores = users.potential_stores_for_load(&opt_ctx.ctx, load);
|
||||
|
||||
if clif_comments.enabled() {
|
||||
for &store in &potential_stores {
|
||||
clif_comments.add_comment(
|
||||
load,
|
||||
format!(
|
||||
"Potential store -> load forwarding {} -> {} ({:?}, {:?})",
|
||||
opt_ctx.ctx.func.dfg.display_inst(store, None),
|
||||
opt_ctx.ctx.func.dfg.display_inst(load, None),
|
||||
spatial_overlap(&opt_ctx.ctx.func, store, load),
|
||||
temporal_order(&opt_ctx.ctx, store, load),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match *potential_stores {
|
||||
[] => {
|
||||
if clif_comments.enabled() {
|
||||
clif_comments
|
||||
.add_comment(load, "[BUG?] Reading uninitialized memory".to_string());
|
||||
}
|
||||
}
|
||||
[store]
|
||||
if spatial_overlap(&opt_ctx.ctx.func, store, load) == SpatialOverlap::Full
|
||||
&& temporal_order(&opt_ctx.ctx, store, load)
|
||||
== TemporalOrder::DefinitivelyBefore =>
|
||||
{
|
||||
// Only one store could have been the origin of the value.
|
||||
let stored_value = opt_ctx.ctx.func.dfg.inst_args(store)[0];
|
||||
|
||||
if clif_comments.enabled() {
|
||||
clif_comments.add_comment(
|
||||
load,
|
||||
format!("Store to load forward {} -> {}", store, load),
|
||||
);
|
||||
}
|
||||
|
||||
users.change_load_to_alias(&mut opt_ctx.ctx.func, load, stored_value);
|
||||
}
|
||||
_ => {} // FIXME implement this
|
||||
}
|
||||
}
|
||||
|
||||
for store in users.stack_store.clone().into_iter() {
|
||||
let potential_loads = users.potential_loads_of_store(&opt_ctx.ctx, store);
|
||||
|
||||
if clif_comments.enabled() {
|
||||
for &load in &potential_loads {
|
||||
clif_comments.add_comment(
|
||||
store,
|
||||
format!(
|
||||
"Potential load from store {} <- {} ({:?}, {:?})",
|
||||
opt_ctx.ctx.func.dfg.display_inst(load, None),
|
||||
opt_ctx.ctx.func.dfg.display_inst(store, None),
|
||||
spatial_overlap(&opt_ctx.ctx.func, store, load),
|
||||
temporal_order(&opt_ctx.ctx, store, load),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if potential_loads.is_empty() {
|
||||
// Never loaded; can safely remove all stores and the stack slot.
|
||||
// FIXME also remove stores when there is always a next store before a load.
|
||||
|
||||
if clif_comments.enabled() {
|
||||
clif_comments.add_comment(
|
||||
store,
|
||||
format!(
|
||||
"Remove dead stack store {} of {}",
|
||||
opt_ctx.ctx.func.dfg.display_inst(store, None),
|
||||
stack_slot.0
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
users.remove_dead_store(&mut opt_ctx.ctx.func, store);
|
||||
}
|
||||
}
|
||||
|
||||
if users.stack_store.is_empty() && users.stack_load.is_empty() {
|
||||
opt_ctx.ctx.func.stack_slots[stack_slot.0].size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn combine_stack_addr_with_load_store(func: &mut Function) {
|
||||
// Turn load and store into stack_load and stack_store when possible.
|
||||
let mut cursor = FuncCursor::new(func);
|
||||
while let Some(_block) = cursor.next_block() {
|
||||
while let Some(inst) = cursor.next_inst() {
|
||||
match cursor.func.dfg[inst] {
|
||||
InstructionData::Load { opcode: Opcode::Load, arg: addr, flags: _, offset } => {
|
||||
if cursor.func.dfg.ctrl_typevar(inst) == types::I128
|
||||
|| cursor.func.dfg.ctrl_typevar(inst).is_vector()
|
||||
{
|
||||
continue; // WORKAROUD: stack_load.i128 not yet implemented
|
||||
}
|
||||
if let Some((stack_slot, stack_addr_offset)) =
|
||||
try_get_stack_slot_and_offset_for_addr(cursor.func, addr)
|
||||
{
|
||||
if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into())
|
||||
{
|
||||
let ty = cursor.func.dfg.ctrl_typevar(inst);
|
||||
cursor.func.dfg.replace(inst).stack_load(
|
||||
ty,
|
||||
stack_slot,
|
||||
combined_offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
InstructionData::Store {
|
||||
opcode: Opcode::Store,
|
||||
args: [value, addr],
|
||||
flags: _,
|
||||
offset,
|
||||
} => {
|
||||
if cursor.func.dfg.ctrl_typevar(inst) == types::I128
|
||||
|| cursor.func.dfg.ctrl_typevar(inst).is_vector()
|
||||
{
|
||||
continue; // WORKAROUND: stack_store.i128 not yet implemented
|
||||
}
|
||||
if let Some((stack_slot, stack_addr_offset)) =
|
||||
try_get_stack_slot_and_offset_for_addr(cursor.func, addr)
|
||||
{
|
||||
if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into())
|
||||
{
|
||||
cursor.func.dfg.replace(inst).stack_store(
|
||||
value,
|
||||
stack_slot,
|
||||
combined_offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) {
|
||||
// FIXME incrementally rebuild on each call?
|
||||
let mut stack_addr_load_insts_users = FxHashMap::<Inst, FxHashSet<Inst>>::default();
|
||||
|
||||
let mut cursor = FuncCursor::new(&mut opt_ctx.ctx.func);
|
||||
while let Some(_block) = cursor.next_block() {
|
||||
while let Some(inst) = cursor.next_inst() {
|
||||
for &arg in cursor.func.dfg.inst_args(inst) {
|
||||
if let ValueDef::Result(arg_origin, 0) = cursor.func.dfg.value_def(arg) {
|
||||
match cursor.func.dfg[arg_origin].opcode() {
|
||||
Opcode::StackAddr | Opcode::StackLoad => {
|
||||
stack_addr_load_insts_users
|
||||
.entry(arg_origin)
|
||||
.or_insert_with(FxHashSet::default)
|
||||
.insert(inst);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
for inst in stack_addr_load_insts_users.keys() {
|
||||
let mut is_recorded_stack_addr_or_stack_load = false;
|
||||
for stack_slot_users in opt_ctx.stack_slot_usage_map.values() {
|
||||
is_recorded_stack_addr_or_stack_load |= stack_slot_users.stack_addr.contains(inst)
|
||||
|| stack_slot_users.stack_load.contains(inst);
|
||||
}
|
||||
assert!(is_recorded_stack_addr_or_stack_load);
|
||||
}
|
||||
|
||||
// Replace all unused stack_addr and stack_load instructions with nop.
|
||||
let mut func = &mut opt_ctx.ctx.func;
|
||||
|
||||
for stack_slot_users in opt_ctx.stack_slot_usage_map.values_mut() {
|
||||
stack_slot_users
|
||||
.stack_addr
|
||||
.drain_filter(|inst| {
|
||||
stack_addr_load_insts_users.get(inst).map(|users| users.is_empty()).unwrap_or(true)
|
||||
})
|
||||
.for_each(|inst| StackSlotUsage::remove_unused_stack_addr(&mut func, inst));
|
||||
|
||||
stack_slot_users
|
||||
.stack_load
|
||||
.drain_filter(|inst| {
|
||||
stack_addr_load_insts_users.get(inst).map(|users| users.is_empty()).unwrap_or(true)
|
||||
})
|
||||
.for_each(|inst| StackSlotUsage::remove_unused_load(&mut func, inst));
|
||||
}
|
||||
}
|
||||
|
||||
fn try_get_stack_slot_and_offset_for_addr(
|
||||
func: &Function,
|
||||
addr: Value,
|
||||
) -> Option<(StackSlot, Offset32)> {
|
||||
if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) {
|
||||
if let InstructionData::StackLoad { opcode: Opcode::StackAddr, stack_slot, offset } =
|
||||
func.dfg[addr_inst]
|
||||
{
|
||||
return Some((stack_slot, offset));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum SpatialOverlap {
|
||||
No,
|
||||
Partial,
|
||||
Full,
|
||||
}
|
||||
|
||||
fn spatial_overlap(func: &Function, src: Inst, dest: Inst) -> SpatialOverlap {
|
||||
fn inst_info(func: &Function, inst: Inst) -> (StackSlot, Offset32, u32) {
|
||||
match func.dfg[inst] {
|
||||
InstructionData::StackLoad { opcode: Opcode::StackAddr, stack_slot, offset }
|
||||
| InstructionData::StackLoad { opcode: Opcode::StackLoad, stack_slot, offset }
|
||||
| InstructionData::StackStore {
|
||||
opcode: Opcode::StackStore,
|
||||
stack_slot,
|
||||
offset,
|
||||
arg: _,
|
||||
} => (stack_slot, offset, func.dfg.ctrl_typevar(inst).bytes()),
|
||||
_ => unreachable!("{:?}", func.dfg[inst]),
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert_ne!(src, dest);
|
||||
|
||||
let (src_ss, src_offset, src_size) = inst_info(func, src);
|
||||
let (dest_ss, dest_offset, dest_size) = inst_info(func, dest);
|
||||
|
||||
if src_ss != dest_ss {
|
||||
return SpatialOverlap::No;
|
||||
}
|
||||
|
||||
if src_offset == dest_offset && src_size == dest_size {
|
||||
return SpatialOverlap::Full;
|
||||
}
|
||||
|
||||
let src_end: i64 = src_offset.try_add_i64(i64::from(src_size)).unwrap().into();
|
||||
let dest_end: i64 = dest_offset.try_add_i64(i64::from(dest_size)).unwrap().into();
|
||||
if src_end <= dest_offset.into() || dest_end <= src_offset.into() {
|
||||
return SpatialOverlap::No;
|
||||
}
|
||||
|
||||
SpatialOverlap::Partial
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum TemporalOrder {
|
||||
/// `src` will never be executed before `dest`.
|
||||
NeverBefore,
|
||||
|
||||
/// `src` may be executed before `dest`.
|
||||
MaybeBefore,
|
||||
|
||||
/// `src` will always be executed before `dest`.
|
||||
/// There may still be other instructions in between.
|
||||
DefinitivelyBefore,
|
||||
}
|
||||
|
||||
fn temporal_order(ctx: &Context, src: Inst, dest: Inst) -> TemporalOrder {
|
||||
debug_assert_ne!(src, dest);
|
||||
|
||||
if ctx.domtree.dominates(src, dest, &ctx.func.layout) {
|
||||
TemporalOrder::DefinitivelyBefore
|
||||
} else if ctx.domtree.dominates(src, dest, &ctx.func.layout) {
|
||||
TemporalOrder::NeverBefore
|
||||
} else {
|
||||
TemporalOrder::MaybeBefore
|
||||
}
|
||||
}
|
|
@ -207,7 +207,7 @@ pub(crate) fn should_write_ir(tcx: TyCtxt<'_>) -> bool {
|
|||
|
||||
pub(crate) fn write_ir_file(
|
||||
tcx: TyCtxt<'_>,
|
||||
name: &str,
|
||||
name: impl FnOnce() -> String,
|
||||
write: impl FnOnce(&mut dyn Write) -> std::io::Result<()>,
|
||||
) {
|
||||
if !should_write_ir(tcx) {
|
||||
|
@ -222,7 +222,7 @@ pub(crate) fn write_ir_file(
|
|||
res @ Err(_) => res.unwrap(),
|
||||
}
|
||||
|
||||
let clif_file_name = clif_output_dir.join(name);
|
||||
let clif_file_name = clif_output_dir.join(name());
|
||||
|
||||
let res = std::fs::File::create(clif_file_name).and_then(|mut file| write(&mut file));
|
||||
if let Err(err) = res {
|
||||
|
@ -238,30 +238,31 @@ pub(crate) fn write_clif_file<'tcx>(
|
|||
context: &cranelift_codegen::Context,
|
||||
mut clif_comments: &CommentWriter,
|
||||
) {
|
||||
write_ir_file(tcx, &format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), |file| {
|
||||
let value_ranges =
|
||||
isa.map(|isa| context.build_value_labels_ranges(isa).expect("value location ranges"));
|
||||
write_ir_file(
|
||||
tcx,
|
||||
|| format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix),
|
||||
|file| {
|
||||
let value_ranges = isa
|
||||
.map(|isa| context.build_value_labels_ranges(isa).expect("value location ranges"));
|
||||
|
||||
let mut clif = String::new();
|
||||
cranelift_codegen::write::decorate_function(
|
||||
&mut clif_comments,
|
||||
&mut clif,
|
||||
&context.func,
|
||||
&DisplayFunctionAnnotations {
|
||||
isa: Some(&*crate::build_isa(tcx.sess)),
|
||||
value_ranges: value_ranges.as_ref(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let mut clif = String::new();
|
||||
cranelift_codegen::write::decorate_function(
|
||||
&mut clif_comments,
|
||||
&mut clif,
|
||||
&context.func,
|
||||
&DisplayFunctionAnnotations { isa, value_ranges: value_ranges.as_ref() },
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
writeln!(file, "test compile")?;
|
||||
writeln!(file, "set is_pic")?;
|
||||
writeln!(file, "set enable_simd")?;
|
||||
writeln!(file, "target {} haswell", crate::target_triple(tcx.sess))?;
|
||||
writeln!(file)?;
|
||||
file.write_all(clif.as_bytes())?;
|
||||
Ok(())
|
||||
});
|
||||
writeln!(file, "test compile")?;
|
||||
writeln!(file, "set is_pic")?;
|
||||
writeln!(file, "set enable_simd")?;
|
||||
writeln!(file, "target {} haswell", crate::target_triple(tcx.sess))?;
|
||||
writeln!(file)?;
|
||||
file.write_all(clif.as_bytes())?;
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
impl fmt::Debug for FunctionCx<'_, '_, '_> {
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::prelude::*;
|
|||
|
||||
fn codegen_print(fx: &mut FunctionCx<'_, '_, '_>, msg: &str) {
|
||||
let puts = fx
|
||||
.cx
|
||||
.module
|
||||
.declare_function(
|
||||
"puts",
|
||||
|
@ -16,13 +15,12 @@ fn codegen_print(fx: &mut FunctionCx<'_, '_, '_>, msg: &str) {
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
let puts = fx.cx.module.declare_func_in_func(puts, &mut fx.bcx.func);
|
||||
let puts = fx.module.declare_func_in_func(puts, &mut fx.bcx.func);
|
||||
if fx.clif_comments.enabled() {
|
||||
fx.add_comment(puts, "puts");
|
||||
}
|
||||
|
||||
let symbol_name = fx.tcx.symbol_name(fx.instance);
|
||||
let real_msg = format!("trap at {:?} ({}): {}\0", fx.instance, symbol_name, msg);
|
||||
let real_msg = format!("trap at {:?} ({}): {}\0", fx.instance, fx.symbol_name, msg);
|
||||
let msg_ptr = fx.anonymous_str("trap", &real_msg);
|
||||
fx.bcx.ins().call(puts, &[msg_ptr]);
|
||||
}
|
||||
|
|
|
@ -554,7 +554,7 @@ impl<'tcx> CPlace<'tcx> {
|
|||
let src_align = src_layout.align.abi.bytes() as u8;
|
||||
let dst_align = dst_layout.align.abi.bytes() as u8;
|
||||
fx.bcx.emit_small_memory_copy(
|
||||
fx.cx.module.target_config(),
|
||||
fx.module.target_config(),
|
||||
to_addr,
|
||||
from_addr,
|
||||
size,
|
||||
|
|
|
@ -72,15 +72,15 @@ pub(crate) fn get_vtable<'tcx>(
|
|||
layout: TyAndLayout<'tcx>,
|
||||
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
||||
) -> Value {
|
||||
let data_id = if let Some(data_id) = fx.cx.vtables.get(&(layout.ty, trait_ref)) {
|
||||
let data_id = if let Some(data_id) = fx.vtables.get(&(layout.ty, trait_ref)) {
|
||||
*data_id
|
||||
} else {
|
||||
let data_id = build_vtable(fx, layout, trait_ref);
|
||||
fx.cx.vtables.insert((layout.ty, trait_ref), data_id);
|
||||
fx.vtables.insert((layout.ty, trait_ref), data_id);
|
||||
data_id
|
||||
};
|
||||
|
||||
let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ fn build_vtable<'tcx>(
|
|||
|
||||
let drop_in_place_fn = import_function(
|
||||
tcx,
|
||||
fx.cx.module,
|
||||
fx.module,
|
||||
Instance::resolve_drop_in_place(tcx, layout.ty).polymorphize(fx.tcx),
|
||||
);
|
||||
|
||||
|
@ -111,7 +111,7 @@ fn build_vtable<'tcx>(
|
|||
opt_mth.map(|(def_id, substs)| {
|
||||
import_function(
|
||||
tcx,
|
||||
fx.cx.module,
|
||||
fx.module,
|
||||
Instance::resolve_for_vtable(tcx, ParamEnv::reveal_all(), def_id, substs)
|
||||
.unwrap()
|
||||
.polymorphize(fx.tcx),
|
||||
|
@ -132,34 +132,16 @@ fn build_vtable<'tcx>(
|
|||
|
||||
for (i, component) in components.into_iter().enumerate() {
|
||||
if let Some(func_id) = component {
|
||||
let func_ref = fx.cx.module.declare_func_in_data(func_id, &mut data_ctx);
|
||||
let func_ref = fx.module.declare_func_in_data(func_id, &mut data_ctx);
|
||||
data_ctx.write_function_addr((i * usize_size) as u32, func_ref);
|
||||
}
|
||||
}
|
||||
|
||||
data_ctx.set_align(fx.tcx.data_layout.pointer_align.pref.bytes());
|
||||
|
||||
let data_id = fx
|
||||
.cx
|
||||
.module
|
||||
.declare_data(
|
||||
&format!(
|
||||
"__vtable.{}.for.{:?}.{}",
|
||||
trait_ref
|
||||
.as_ref()
|
||||
.map(|trait_ref| format!("{:?}", trait_ref.skip_binder()).into())
|
||||
.unwrap_or(std::borrow::Cow::Borrowed("???")),
|
||||
layout.ty,
|
||||
fx.cx.vtables.len(),
|
||||
),
|
||||
Linkage::Local,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let data_id = fx.module.declare_anonymous_data(false, false).unwrap();
|
||||
|
||||
// FIXME don't duplicate definitions in lazy jit mode
|
||||
let _ = fx.cx.module.define_data(data_id, &data_ctx);
|
||||
fx.module.define_data(data_id, &data_ctx).unwrap();
|
||||
|
||||
data_id
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue