message: FunctionCx: WIP: Remove more common fields from CodegenCx

diff:
@@ -226,7 +226,7 @@ pub(crate) fn import_function<'tcx>(
impl<'tcx, B: Backend + 'static> FunctionCx<'_, 'tcx, B> {
/// Instance must be monomorphized
pub(crate) fn get_function_ref(&mut self, inst: Instance<'tcx>) -> FuncRef {
- let func_id = import_function(self.codegen_cx.tcx, self.codegen_cx.module, inst);
+ let func_id = import_function(self.codegen_cx.tcx, &mut self.codegen_cx.module, inst);
let func_ref = self
.codegen_cx.module
.declare_func_in_func(func_id, &mut self.bcx.func);

diff:
@@ -8,13 +8,13 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
instance: Instance<'tcx>,
linkage: Linkage,
) {
- let tcx = cx.codegen_cx.tcx;
+ let tcx = cx.tcx;
let mir = tcx.instance_mir(instance.def);
// Declare function
- let (name, sig) = get_function_name_and_sig(tcx, cx.codegen_cx.module.isa().triple(), instance, false);
- let func_id = cx.codegen_cx.module.declare_function(&name, linkage, &sig).unwrap();
+ let (name, sig) = get_function_name_and_sig(tcx, cx.module.isa().triple(), instance, false);
+ let func_id = cx.module.declare_function(&name, linkage, &sig).unwrap();
// Make FunctionBuilder
let context = &mut cx.cached_context;
@@ -30,13 +30,11 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let block_map: IndexVec<BasicBlock, Block> = (0..mir.basic_blocks().len()).map(|_| bcx.create_block()).collect();
// Make FunctionCx
- let pointer_type = cx.codegen_cx.module.target_config().pointer_type();
+ let pointer_type = cx.module.target_config().pointer_type();
let clif_comments = crate::pretty_clif::CommentWriter::new(tcx, instance);
let mut fx = FunctionCx {
- tcx,
- module: &mut cx.codegen_cx.module,
- global_asm: &mut cx.global_asm,
+ codegen_cx: cx,
pointer_type,
instance,
@@ -49,8 +47,6 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
cold_blocks: EntitySet::new(),
clif_comments,
- constants_cx: &mut cx.constants_cx,
- vtables: &mut cx.vtables,
source_info_set: indexmap::IndexSet::new(),
next_ssa_var: 0,
@@ -78,7 +74,7 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let cold_blocks = fx.cold_blocks;
crate::pretty_clif::write_clif_file(
- cx.codegen_cx.tcx,
+ cx.tcx,
"unopt",
None,
instance,
@@ -98,10 +94,10 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
// instruction, which doesn't have an encoding.
context.compute_cfg();
context.compute_domtree();
- context.eliminate_unreachable_code(cx.codegen_cx.module.isa()).unwrap();
+ context.eliminate_unreachable_code(cx.module.isa()).unwrap();
// Define function
- let module = &mut cx.codegen_cx.module;
+ let module = &mut cx.module;
tcx.sess.time(
"define function",
|| module.define_function(
@@ -113,16 +109,16 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
// Write optimized function to file for debugging
crate::pretty_clif::write_clif_file(
- cx.codegen_cx.tcx,
+ cx.tcx,
"opt",
- Some(cx.codegen_cx.module.isa()),
+ Some(cx.module.isa()),
instance,
&context,
&clif_comments,
);
// Define debuginfo for function
- let isa = cx.codegen_cx.module.isa();
+ let isa = cx.module.isa();
let debug_context = &mut cx.debug_context;
let unwind_context = &mut cx.unwind_context;
tcx.sess.time("generate debug info", || {

diff:
@@ -266,7 +266,6 @@ pub(crate) fn type_sign(ty: Ty<'_>) -> bool {
pub(crate) struct FunctionCx<'clif, 'tcx, B: Backend + 'static> {
pub(crate) codegen_cx: &'clif mut crate::CodegenCx<'tcx, B>,
- pub(crate) global_asm: &'clif mut String,
pub(crate) pointer_type: Type, // Cached from module
pub(crate) instance: Instance<'tcx>,
@@ -283,8 +282,6 @@ pub(crate) struct FunctionCx<'clif, 'tcx, B: Backend + 'static> {
pub(crate) cold_blocks: EntitySet<Block>,
pub(crate) clif_comments: crate::pretty_clif::CommentWriter,
- pub(crate) vtables: &'clif mut FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), DataId>,
-
pub(crate) source_info_set: indexmap::IndexSet<SourceInfo>,
/// This should only be accessed by `CPlace::new_var`.

diff:
@@ -67,7 +67,7 @@ pub(crate) fn codegen_tls_ref<'tcx>(
def_id: DefId,
layout: TyAndLayout<'tcx>,
) -> CValue<'tcx> {
- let data_id = data_id_for_static(fx.codegen_cx.tcx, fx.codegen_cx.module, def_id, false);
+ let data_id = data_id_for_static(fx.codegen_cx.tcx, &mut fx.codegen_cx.module, def_id, false);
let local_data_id = fx.codegen_cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
fx.add_comment(local_data_id, format!("tls {:?}", def_id));
@@ -80,7 +80,7 @@ fn codegen_static_ref<'tcx>(
def_id: DefId,
layout: TyAndLayout<'tcx>,
) -> CPlace<'tcx> {
- let data_id = data_id_for_static(fx.codegen_cx.tcx, fx.codegen_cx.module, def_id, false);
+ let data_id = data_id_for_static(fx.codegen_cx.tcx, &mut fx.codegen_cx.module, def_id, false);
let local_data_id = fx.codegen_cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
fx.add_comment(local_data_id, format!("{:?}", def_id));
@@ -167,21 +167,21 @@ pub(crate) fn trans_const_value<'tcx>(
let alloc_kind = fx.codegen_cx.tcx.get_global_alloc(ptr.alloc_id);
let base_addr = match alloc_kind {
Some(GlobalAlloc::Memory(alloc)) => {
- fx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id));
- let data_id = data_id_for_alloc_id(fx.codegen_cx.module, ptr.alloc_id, alloc.align, alloc.mutability);
+ fx.codegen_cx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id));
+ let data_id = data_id_for_alloc_id(&mut fx.codegen_cx.module, ptr.alloc_id, alloc.align, alloc.mutability);
let local_data_id = fx.codegen_cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
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.codegen_cx.tcx, fx.codegen_cx.module, instance);
+ let func_id = crate::abi::import_function(fx.codegen_cx.tcx, &mut fx.codegen_cx.module, instance);
let local_func_id = fx.codegen_cx.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.codegen_cx.tcx.is_static(def_id));
- let data_id = data_id_for_static(fx.codegen_cx.tcx, fx.codegen_cx.module, def_id, false);
+ let data_id = data_id_for_static(fx.codegen_cx.tcx, &mut fx.codegen_cx.module, def_id, false);
let local_data_id = fx.codegen_cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
fx.add_comment(local_data_id, format!("{:?}", def_id));
@@ -216,8 +216,8 @@ fn pointer_for_allocation<'tcx>(
alloc: &'tcx Allocation,
) -> crate::pointer::Pointer {
let alloc_id = fx.codegen_cx.tcx.create_memory_alloc(alloc);
- fx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
- let data_id = data_id_for_alloc_id(fx.codegen_cx.module, alloc_id, alloc.align, alloc.mutability);
+ fx.codegen_cx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
+ let data_id = data_id_for_alloc_id(&mut fx.codegen_cx.module, alloc_id, alloc.align, alloc.mutability);
let local_data_id = fx.codegen_cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]

diff:
@@ -95,7 +95,7 @@ fn line_program_add_file(
impl<'tcx> DebugContext<'tcx> {
pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) {
- let loc = self.codegen_cx.tcx.sess.source_map().lookup_char_pos(span.lo());
+ let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo());
let file_id = line_program_add_file(
&mut self.dwarf.unit.line_program,
@@ -129,7 +129,7 @@ pub(super) fn create_debug_lines(
function_span: Span,
source_info_set: &indexmap::IndexSet<SourceInfo>,
) -> CodeOffset {
- let tcx = self.codegen_cx.tcx;
+ let tcx = self.tcx;
let line_program = &mut self.dwarf.unit.line_program;
let func = &context.func;

diff:
@@ -165,7 +165,7 @@ fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
};
let name = format!("{}", ty);
- let layout = self.codegen_cx.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();
+ let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();
let type_id = match ty.kind {
ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean),
@@ -203,7 +203,7 @@ fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
for (field_idx, field_def) in variant.fields.iter().enumerate() {
let field_offset = layout.fields.offset(field_idx);
let field_layout = layout.field(&layout::LayoutCx {
- tcx: self.codegen_cx.tcx,
+ tcx: self.tcx,
param_env: ParamEnv::reveal_all(),
}, field_idx).unwrap();
@@ -261,7 +261,7 @@ pub(crate) fn define_function(
local_map: FxHashMap<mir::Local, CPlace<'tcx>>,
) {
let symbol = func_id.as_u32() as usize;
- let mir = self.codegen_cx.tcx.instance_mir(instance.def);
+ let mir = self.tcx.instance_mir(instance.def);
// FIXME: add to appropriate scope instead of root
let scope = self.dwarf.unit.root();
@@ -336,7 +336,7 @@ pub(crate) fn define_function(
let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap();
for (local, _local_decl) in mir.local_decls.iter_enumerated() {
- let ty = self.codegen_cx.tcx.subst_and_normalize_erasing_regions(
+ let ty = self.tcx.subst_and_normalize_erasing_regions(
instance.substs,
ty::ParamEnv::reveal_all(),
&mir.local_decls[local].ty,

diff:
@@ -54,7 +54,7 @@ pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: &
}
pub(crate) fn emit<P: WriteDebugInfo>(self, product: &mut P) {
- let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.codegen_cx.tcx)));
+ let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.tcx)));
self.frame_table.write_eh_frame(&mut eh_frame).unwrap();
if !eh_frame.0.writer.slice().is_empty() {
@@ -74,7 +74,7 @@ pub(crate) unsafe fn register_jit(
self,
jit_module: &mut Module<cranelift_simplejit::SimpleJITBackend>,
) -> Option<UnwindRegistry> {
- let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.codegen_cx.tcx)));
+ let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.tcx)));
self.frame_table.write_eh_frame(&mut eh_frame).unwrap();
if eh_frame.0.writer.slice().is_empty() {

diff:
@@ -33,14 +33,14 @@ fn codegen_mono_items<'tcx>(
cx: &mut crate::CodegenCx<'tcx, impl Backend + 'static>,
mono_items: Vec<(MonoItem<'tcx>, (RLinkage, Visibility))>,
) {
- cx.codegen_cx.tcx.sess.time("predefine functions", || {
+ cx.tcx.sess.time("predefine functions", || {
for &(mono_item, (linkage, visibility)) in &mono_items {
match mono_item {
MonoItem::Fn(instance) => {
let (name, sig) =
- get_function_name_and_sig(cx.codegen_cx.tcx, cx.codegen_cx.module.isa().triple(), instance, false);
+ get_function_name_and_sig(cx.tcx, cx.module.isa().triple(), instance, false);
let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility);
- cx.codegen_cx.module.declare_function(&name, linkage, &sig).unwrap();
+ cx.module.declare_function(&name, linkage, &sig).unwrap();
}
MonoItem::Static(_) | MonoItem::GlobalAsm(_) => {}
}
@@ -58,7 +58,7 @@ fn trans_mono_item<'tcx, B: Backend + 'static>(
mono_item: MonoItem<'tcx>,
linkage: Linkage,
) {
- let tcx = cx.codegen_cx.tcx;
+ let tcx = cx.tcx;
match mono_item {
MonoItem::Fn(inst) => {
let _inst_guard =
@@ -85,7 +85,7 @@ fn trans_mono_item<'tcx, B: Backend + 'static>(
}
});
- cx.codegen_cx.tcx.sess.time("codegen fn", || crate::base::trans_fn(cx, inst, linkage));
+ cx.tcx.sess.time("codegen fn", || crate::base::trans_fn(cx, inst, linkage));
}
MonoItem::Static(def_id) => {
crate::constant::codegen_static(&mut cx.constants_cx, def_id);

diff:
@@ -160,8 +160,8 @@ fn new(
}
fn finalize(mut self) -> (Module<B>, String, Option<DebugContext<'tcx>>, UnwindContext<'tcx>) {
- self.constants_cx.finalize(self.codegen_cx.tcx, &mut self.codegen_cx.module);
- (self.codegen_cx.module, self.global_asm, self.debug_context, self.unwind_context)
+ self.constants_cx.finalize(self.tcx, &mut self.module);
+ (self.module, self.global_asm, self.debug_context, self.unwind_context)
}
}

diff:
@@ -93,7 +93,7 @@ fn build_vtable<'tcx>(
let usize_size = fx.layout_of(fx.codegen_cx.tcx.types.usize).size.bytes() as usize;
let drop_in_place_fn =
- import_function(tcx, fx.codegen_cx.module, Instance::resolve_drop_in_place(tcx, layout.ty).polymorphize(fx.codegen_cx.tcx));
+ import_function(tcx, &mut fx.codegen_cx.module, Instance::resolve_drop_in_place(tcx, layout.ty).polymorphize(fx.codegen_cx.tcx));
let mut components: Vec<_> = vec![Some(drop_in_place_fn), None, None];
@@ -108,7 +108,7 @@ fn build_vtable<'tcx>(
opt_mth.map_or(None, |(def_id, substs)| {
Some(import_function(
tcx,
- fx.codegen_cx.module,
+ &mut fx.codegen_cx.module,
Instance::resolve_for_vtable(tcx, ParamEnv::reveal_all(), def_id, substs).unwrap().polymorphize(fx.codegen_cx.tcx),
))
})


message: FunctionCx: Fix field usage in inline_asm and vtable modules

change_type: MODIFY

diff:
@@ -73,7 +73,7 @@ pub(crate) fn codegen_inline_asm<'tcx>(
let asm_name = format!("{}__inline_asm_{}", fx.codegen_cx.tcx.symbol_name(fx.instance).name, inline_asm_index);
let generated_asm = generate_asm_wrapper(&asm_name, InlineAsmArch::X86_64, options, template, clobbered_regs, &inputs, &outputs);
- fx.global_asm.push_str(&generated_asm);
+ fx.codegen_cx.global_asm.push_str(&generated_asm);
call_inline_asm(fx, &asm_name, slot_size, inputs, outputs);
}

diff:
@@ -72,11 +72,11 @@ 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.vtables.get(&(layout.ty, trait_ref)) {
+ let data_id = if let Some(data_id) = fx.codegen_cx.vtables.get(&(layout.ty, trait_ref)) {
*data_id
} else {
let data_id = build_vtable(fx, layout, trait_ref);
- fx.vtables.insert((layout.ty, trait_ref), data_id);
+ fx.codegen_cx.vtables.insert((layout.ty, trait_ref), data_id);
data_id
};
@@ -142,7 +142,7 @@ fn build_vtable<'tcx>(
.map(|trait_ref| format!("{:?}", trait_ref.skip_binder()).into())
.unwrap_or(std::borrow::Cow::Borrowed("???")),
layout.ty,
- fx.vtables.len(),
+ fx.codegen_cx.vtables.len(),
),
Linkage::Local,
false,

license: Apache License 2.0


message: FunctionCx: Fix FunctionCx initialization in trans_fn()

diff:
@@ -16,14 +16,9 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let (name, sig) = get_function_name_and_sig(tcx, cx.module.isa().triple(), instance, false);
let func_id = cx.module.declare_function(&name, linkage, &sig).unwrap();
- // Make FunctionBuilder
- let context = &mut cx.cached_context;
- context.clear();
- context.func.name = ExternalName::user(0, func_id.as_u32());
- context.func.signature = sig;
- context.func.collect_debug_info();
let mut func_ctx = FunctionBuilderContext::new();
- let mut bcx = FunctionBuilder::new(&mut context.func, &mut func_ctx);
+ let mut func = Function::new();
+ let mut bcx = FunctionBuilder::new(&mut func, &mut func_ctx);
// Predefine blocks
let start_block = bcx.create_block();
@@ -66,6 +61,13 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
});
}
+ // Make FunctionBuilder
+ let context = &mut fx.cx.cached_context;
+ context.clear();
+ context.func.name = ExternalName::user(0, func_id.as_u32());
+ context.func.signature = sig;
+ context.func.collect_debug_info();
+
// Recover all necessary data from fx, before accessing func will prevent future access to it.
let instance = fx.instance;
let mut clif_comments = fx.clif_comments;
@@ -74,7 +76,7 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let cold_blocks = fx.cold_blocks;
crate::pretty_clif::write_clif_file(
- cx.tcx,
+ fx.cx.tcx,
"unopt",
None,
instance,
@@ -94,10 +96,10 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
// instruction, which doesn't have an encoding.
context.compute_cfg();
context.compute_domtree();
- context.eliminate_unreachable_code(cx.module.isa()).unwrap();
+ context.eliminate_unreachable_code(fx.cx.module.isa()).unwrap();
// Define function
- let module = &mut cx.module;
+ let module = &mut fx.cx.module;
tcx.sess.time(
"define function",
|| module.define_function(
@@ -109,18 +111,18 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
// Write optimized function to file for debugging
crate::pretty_clif::write_clif_file(
- cx.tcx,
+ fx.cx.tcx,
"opt",
- Some(cx.module.isa()),
+ Some(fx.cx.module.isa()),
instance,
&context,
&clif_comments,
);
// Define debuginfo for function
- let isa = cx.module.isa();
- let debug_context = &mut cx.debug_context;
- let unwind_context = &mut cx.unwind_context;
+ let isa = fx.cx.module.isa();
+ let debug_context = &mut fx.cx.debug_context;
+ let unwind_context = &mut fx.cx.unwind_context;
tcx.sess.time("generate debug info", || {
if let Some(debug_context) = debug_context {
debug_context.define_function(instance, func_id, &name, isa, context, &source_info_set, local_map);


message: FunctionCx: Add tcx field for easier access

diff:
@@ -30,6 +30,7 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let mut fx = FunctionCx {
cx,
+ tcx,
pointer_type,
instance,

diff:
@@ -266,6 +266,7 @@ pub(crate) fn type_sign(ty: Ty<'_>) -> bool {
pub(crate) struct FunctionCx<'clif, 'tcx, B: Backend + 'static> {
pub(crate) cx: &'clif mut crate::CodegenCx<'tcx, B>,
+ pub(crate) tcx: TyCtxt<'tcx>,
pub(crate) pointer_type: Type, // Cached from module
pub(crate) instance: Instance<'tcx>,


message: FunctionCx: Swap context.func and empty function correctly

diff:
@@ -16,8 +16,16 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let (name, sig) = get_function_name_and_sig(tcx, cx.module.isa().triple(), instance, false);
let func_id = cx.module.declare_function(&name, linkage, &sig).unwrap();
+ // Make the FunctionBuilder
let mut func_ctx = FunctionBuilderContext::new();
+ cx.cached_context.clear();
+
let mut func = Function::new();
+ std::mem::swap(&mut cx.cached_context.func, &mut func);
+ func.name = ExternalName::user(0, func_id.as_u32());
+ func.signature = sig;
+ func.collect_debug_info();
+
let mut bcx = FunctionBuilder::new(&mut func, &mut func_ctx);
// Predefine blocks
@@ -62,13 +70,6 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
});
}
- // Make FunctionBuilder
- let context = &mut fx.cx.cached_context;
- context.clear();
- context.func.name = ExternalName::user(0, func_id.as_u32());
- context.func.signature = sig;
- context.func.collect_debug_info();
-
// Recover all necessary data from fx, before accessing func will prevent future access to it.
let instance = fx.instance;
let mut clif_comments = fx.clif_comments;
@@ -76,8 +77,12 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let local_map = fx.local_map;
let cold_blocks = fx.cold_blocks;
+ // Store function in context
+ let context = &mut cx.cached_context;
+ context.func = func;
+
crate::pretty_clif::write_clif_file(
- fx.tcx,
+ tcx,
"unopt",
None,
instance,
@@ -97,10 +102,10 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
// instruction, which doesn't have an encoding.
context.compute_cfg();
context.compute_domtree();
- context.eliminate_unreachable_code(fx.cx.module.isa()).unwrap();
+ context.eliminate_unreachable_code(cx.module.isa()).unwrap();
// Define function
- let module = &mut fx.cx.module;
+ let module = &mut cx.module;
tcx.sess.time(
"define function",
|| module.define_function(
@@ -112,18 +117,18 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
// Write optimized function to file for debugging
crate::pretty_clif::write_clif_file(
- fx.tcx,
+ tcx,
"opt",
- Some(fx.cx.module.isa()),
+ Some(cx.module.isa()),
instance,
&context,
&clif_comments,
);
// Define debuginfo for function
- let isa = fx.cx.module.isa();
- let debug_context = &mut fx.cx.debug_context;
- let unwind_context = &mut fx.cx.unwind_context;
+ let isa = cx.module.isa();
+ let debug_context = &mut cx.debug_context;
+ let unwind_context = &mut cx.unwind_context;
tcx.sess.time("generate debug info", || {
if let Some(debug_context) = debug_context {
debug_context.define_function(instance, func_id, &name, isa, context, &source_info_set, local_map);


message: trans_fn: Use replace() instead of new() + swap()

diff:
@@ -20,8 +20,7 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let mut func_ctx = FunctionBuilderContext::new();
cx.cached_context.clear();
- let mut func = Function::new();
- std::mem::swap(&mut cx.cached_context.func, &mut func);
+ let mut func = std::mem::replace(&mut cx.cached_context.func, Function::new());
func.name = ExternalName::user(0, func_id.as_u32());
func.signature = sig;
func.collect_debug_info();


message: trans_fn: Cleanup code

diff:
@@ -16,10 +16,10 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
let (name, sig) = get_function_name_and_sig(tcx, cx.module.isa().triple(), instance, false);
let func_id = cx.module.declare_function(&name, linkage, &sig).unwrap();
- // Make the FunctionBuilder
- let mut func_ctx = FunctionBuilderContext::new();
cx.cached_context.clear();
+ // Make the FunctionBuilder
+ let mut func_ctx = FunctionBuilderContext::new();
let mut func = std::mem::replace(&mut cx.cached_context.func, Function::new());
func.name = ExternalName::user(0, func_id.as_u32());
func.signature = sig;

original_message: trans_fn: Cleanup code


message: remove_unused_stack_addr_and_stack_load: Remove clone()

diff:
@@ -315,14 +315,13 @@ fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) {
// Replace all unused stack_addr and stack_load instructions with nop.
for stack_slot_users in opt_ctx.stack_slot_usage_map.values_mut() {
- // FIXME remove clone
- for &inst in stack_slot_users.stack_addr.clone().iter() {
+ while let Some(&inst) = stack_slot_users.stack_addr.iter().next() {
if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
stack_slot_users.remove_unused_stack_addr(&mut opt_ctx.ctx.func, inst);
}
}
- for &inst in stack_slot_users.stack_load.clone().iter() {
+ while let Some(&inst) = stack_slot_users.stack_load.iter().next() {
if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
stack_slot_users.remove_unused_load(&mut opt_ctx.ctx.func, inst);
}


message: stack2reg: Re-add clone() to stop CI

diff:
@@ -315,13 +315,14 @@ fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) {
// Replace all unused stack_addr and stack_load instructions with nop.
for stack_slot_users in opt_ctx.stack_slot_usage_map.values_mut() {
- while let Some(&inst) = stack_slot_users.stack_addr.iter().next() {
+ // FIXME: Remove clone
+ for &inst in stack_slot_users.stack_addr.clone().iter() {
if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
stack_slot_users.remove_unused_stack_addr(&mut opt_ctx.ctx.func, inst);
}
}
- while let Some(&inst) = stack_slot_users.stack_load.iter().next() {
+ for &inst in stack_slot_users.stack_load.clone().iter() {
if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
stack_slot_users.remove_unused_load(&mut opt_ctx.ctx.func, inst);
}

original_message: stack2reg: Re-add clone() to stop CI


message:
stack2reg: Remove self in remove_unused_stack_addr and()
remove_unused_load()

diff:
@@ -79,16 +79,14 @@ fn potential_loads_of_store(&self, ctx: &Context, store: Inst) -> Vec<Inst> {
}).collect::<Vec<Inst>>()
}
- fn remove_unused_stack_addr(&mut self, func: &mut Function, inst: Inst) {
+ fn remove_unused_stack_addr(func: &mut Function, inst: Inst) {
func.dfg.detach_results(inst);
func.dfg.replace(inst).nop();
- self.stack_addr.remove(&inst);
}
- fn remove_unused_load(&mut self, func: &mut Function, load: Inst) {
+ fn remove_unused_load(func: &mut Function, load: Inst) {
func.dfg.detach_results(load);
func.dfg.replace(load).nop();
- self.stack_load.remove(&load);
}
fn remove_dead_store(&mut self, func: &mut Function, store: Inst) {
@@ -315,18 +313,21 @@ fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) {
// Replace all unused stack_addr and stack_load instructions with nop.
for stack_slot_users in opt_ctx.stack_slot_usage_map.values_mut() {
- // FIXME: Remove clone
for &inst in stack_slot_users.stack_addr.clone().iter() {
if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
- stack_slot_users.remove_unused_stack_addr(&mut opt_ctx.ctx.func, inst);
+ StackSlotUsage::remove_unused_stack_addr(&mut opt_ctx.ctx.func, inst);
+ stack_slot_users.stack_addr.remove(&inst);
}
}
+ /*
for &inst in stack_slot_users.stack_load.clone().iter() {
if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
- stack_slot_users.remove_unused_load(&mut opt_ctx.ctx.func, inst);
+ StackSlotUsage::remove_unused_load(&mut opt_ctx.ctx.func, inst);
+ stack_slot_users.stack_load.remove(&inst);
}
}
+ */
}
}


message: stack2reg: Remove clone() using filters

diff:
@@ -312,22 +312,19 @@ fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) {
}
// 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() {
- for &inst in stack_slot_users.stack_addr.clone().iter() {
- if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
- StackSlotUsage::remove_unused_stack_addr(&mut opt_ctx.ctx.func, inst);
- stack_slot_users.stack_addr.remove(&inst);
- }
- }
-
- /*
- for &inst in stack_slot_users.stack_load.clone().iter() {
- if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
- StackSlotUsage::remove_unused_load(&mut opt_ctx.ctx.func, inst);
- stack_slot_users.stack_load.remove(&inst);
- }
- }
- */
+ stack_slot_users
+ .stack_addr
+ .iter()
+ .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
+ .iter()
+ .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));
}
}

original_message: stack2reg: Remove clone() using filters


message: stack2reg: Drain instead of only iterating

diff:
@@ -316,15 +316,15 @@ fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) {
for stack_slot_users in opt_ctx.stack_slot_usage_map.values_mut() {
stack_slot_users
.stack_addr
- .iter()
+ .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));
+ .for_each(|inst| StackSlotUsage::remove_unused_stack_addr(&mut func, inst));
stack_slot_users
.stack_load
- .iter()
+ .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));
+ .for_each(|inst| StackSlotUsage::remove_unused_load(&mut func, inst));
}
}


message: stack2reg: Switch to hashbrown::HashSet

diff:
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
+[[package]]
+name = "ahash"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
+
[[package]]
name = "anyhow"
version = "1.0.32"
@@ -197,6 +203,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb"
dependencies = [
+ "ahash",
"autocfg",
]
@@ -325,6 +332,7 @@ dependencies = [
"cranelift-object",
"cranelift-simplejit",
"gimli",
+ "hashbrown",
"indexmap",
"libloading",
"object",

diff:
@@ -23,6 +23,7 @@ byteorder = "1.2.7"
indexmap = "1.0.2"
cfg-if = "0.1.10"
libloading = { version = "0.6.0", optional = true }
+hashbrown = "0.8.1"
# Uncomment to use local checkout of cranelift
#[patch."https://github.com/bytecodealliance/wasmtime/"]

diff:
use std::fmt;
use std::ops::Not;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashSet, FxHasher};
use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::ir::{InstructionData, Opcode, ValueDef};
use cranelift_codegen::ir::immediates::Offset32;
+use hashbrown::HashSet;
+use std::hash::BuildHasherDefault;
+
use crate::prelude::*;
/// Workaround for `StackSlot` not implementing `Ord`.
@@ -45,9 +48,9 @@ fn cmp(&self, rhs: &Self) -> std::cmp::Ordering {
#[derive(Debug, Default)]
struct StackSlotUsage {
- stack_addr: FxHashSet<Inst>,
- stack_load: FxHashSet<Inst>,
- stack_store: FxHashSet<Inst>,
+ stack_addr: HashSet<Inst, BuildHasherDefault<FxHasher>>,
+ stack_load: HashSet<Inst, BuildHasherDefault<FxHasher>>,
+ stack_store: HashSet<Inst, BuildHasherDefault<FxHasher>>,
}
impl StackSlotUsage {
@@ -313,17 +316,19 @@ fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) {
// Replace all unused stack_addr and stack_load instructions with nop.
let mut func = &mut opt_ctx.ctx.func;
+
+ // drain_filter() on hashbrown::HashSet drains the items that do *not* match the
+ // predicate. Once hashbrown gets updated to match the behaviour of std::drain_filter
+ // (0.8.2), the predicate will have to be reversed
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))
+ .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))
+ .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));
}
}

original_message: stack2reg: Switch to hashbrown::HashSet


message: fmt: Skip fn_sig_for_fn_abi()

diff:
pub(crate) use self::returning::{can_return_to_ssa_var, codegen_return};
// Copied from https://github.com/rust-lang/rust/blob/f52c72948aa1dd718cc1f168d21c91c584c0a662/src/librustc_middle/ty/layout.rs#L2301
-pub(crate) fn fn_sig_for_fn_abi<'tcx>(
- tcx: TyCtxt<'tcx>,
- instance: Instance<'tcx>,
-) -> ty::PolyFnSig<'tcx> {
+#[rustfmt::skip]
+pub(crate) fn fn_sig_for_fn_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::PolyFnSig<'tcx> {
use rustc_middle::ty::subst::Subst;
// FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function.
@@ -73,10 +71,10 @@ pub(crate) fn fn_sig_for_fn_abi<'tcx>(
let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
sig.map_bound(|sig| {
- let state_did =
- tcx.require_lang_item(rustc_hir::LangItem::GeneratorStateLangItem, None);
+ let state_did = tcx.require_lang_item(rustc_hir::LangItem::GeneratorStateLangItem, None);
let state_adt_ref = tcx.adt_def(state_did);
- let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
+ let state_substs =
+ tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
tcx.mk_fn_sig(

original_message: fmt: Skip fn_sig_for_fn_abi()


message: fmt: Skip src/target_features_whitelist.rs

diff:
+#[rustfmt::skip]
+
use rustc_span::symbol::{sym, Symbol};
use rustc_session::Session;
("vsx", Some(sym::powerpc_target_feature)),
];
-const MIPS_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
- ("fp64", Some(sym::mips_target_feature)),
- ("msa", Some(sym::mips_target_feature)),
-];
+const MIPS_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] =
+ &[("fp64", Some(sym::mips_target_feature)), ("msa", Some(sym::mips_target_feature))];
const RISCV_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
("m", Some(sym::riscv_target_feature)),


diff:
pub(crate) fn optimize_function<'tcx>(
tcx: TyCtxt<'tcx>,
- #[cfg_attr(not(debug_assertions), allow(unused_variables))] instance: Instance<'tcx>,
+ instance: Instance<'tcx>,
ctx: &mut Context,
cold_blocks: &EntitySet<Block>,
clif_comments: &mut crate::pretty_clif::CommentWriter,

original_message:
optimize: Remove conditional compilation on instance

Co-authored-by: bjorn3 <bjorn3@users.noreply.github.com>


message:
Implement `black_box` using intrinsic
The new implementation allows some `memcpy`s to be optimized away,
so the uninit value in ui/sanitize/memory.rs is constructed directly
onto the return place. Therefore the sanitizer now says that the
value is allocated by `main` rather than `random`.

diff:
@@ -1136,6 +1136,11 @@ fn type_by_size(size: Size) -> Option<Type> {
};
ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout()));
};
+
+ black_box, (c a) {
+ // FIXME implement black_box semantics
+ ret.write_cvalue(fx, a);
+ };
}
if let Some((_, dest)) = destination {


message: Introduce NullOp::AlignOf

diff:
@@ -726,15 +726,20 @@ fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
let ptr = fx.bcx.inst_results(call)[0];
lval.write_cvalue(fx, CValue::by_val(ptr, box_layout));
}
- Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
+ Rvalue::NullaryOp(null_op, ty) => {
assert!(
lval.layout()
.ty
.is_sized(fx.tcx.at(stmt.source_info.span), ParamEnv::reveal_all())
);
- let ty_size = fx.layout_of(fx.monomorphize(ty)).size.bytes();
+ let layout = fx.layout_of(fx.monomorphize(ty));
+ let val = match null_op {
+ NullOp::SizeOf => layout.size.bytes(),
+ NullOp::AlignOf => layout.align.abi.bytes(),
+ NullOp::Box => unreachable!(),
+ };
let val =
- CValue::const_val(fx, fx.layout_of(fx.tcx.types.usize), ty_size.into());
+ CValue::const_val(fx, fx.layout_of(fx.tcx.types.usize), val.into());
lval.write_cvalue(fx, val);
}
Rvalue::Aggregate(ref kind, ref operands) => match kind.as_ref() {

diff:
@@ -823,7 +823,7 @@ fn swap(bcx: &mut FunctionBuilder<'_>, v: Value) -> Value {
dest.write_cvalue(fx, val);
};
- pref_align_of | min_align_of | needs_drop | type_id | type_name | variant_count, () {
+ pref_align_of | needs_drop | type_id | type_name | variant_count, () {
let const_val =
fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap();
let val = crate::constant::codegen_const_value(

original_message: Introduce NullOp::AlignOf


message: Introduce `Rvalue::ShallowInitBox`

diff:
@@ -701,6 +701,13 @@ fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
let len = codegen_array_len(fx, place);
lval.write_cvalue(fx, CValue::by_val(len, usize_layout));
}
+ Rvalue::ShallowInitBox(ref operand, content_ty) => {
+ let content_ty = fx.monomorphize(content_ty);
+ let box_layout = fx.layout_of(fx.tcx.mk_box(content_ty));
+ let operand = codegen_operand(fx, operand);
+ let operand = operand.load_scalar(fx);
+ lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
+ }
Rvalue::NullaryOp(NullOp::Box, content_ty) => {
let usize_type = fx.clif_type(fx.tcx.types.usize).unwrap();
let content_ty = fx.monomorphize(content_ty);

original_message: Introduce `Rvalue::ShallowInitBox`


message: Remove NullOp::Box

diff:
@@ -708,30 +708,6 @@ fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
let operand = operand.load_scalar(fx);
lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
}
- Rvalue::NullaryOp(NullOp::Box, content_ty) => {
- let usize_type = fx.clif_type(fx.tcx.types.usize).unwrap();
- let content_ty = fx.monomorphize(content_ty);
- let layout = fx.layout_of(content_ty);
- let llsize = fx.bcx.ins().iconst(usize_type, layout.size.bytes() as i64);
- let llalign = fx.bcx.ins().iconst(usize_type, layout.align.abi.bytes() as i64);
- let box_layout = fx.layout_of(fx.tcx.mk_box(content_ty));
-
- // Allocate space:
- let def_id =
- match fx.tcx.lang_items().require(rustc_hir::LangItem::ExchangeMalloc) {
- Ok(id) => id,
- Err(s) => {
- fx.tcx
- .sess
- .fatal(&format!("allocation of `{}` {}", box_layout.ty, s));
- }
- };
- let instance = ty::Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx);
- let func_ref = fx.get_function_ref(instance);
- let call = fx.bcx.ins().call(func_ref, &[llsize, llalign]);
- let ptr = fx.bcx.inst_results(call)[0];
- lval.write_cvalue(fx, CValue::by_val(ptr, box_layout));
- }
Rvalue::NullaryOp(null_op, ty) => {
assert!(
lval.layout()
@@ -742,7 +718,6 @@ fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
let val = match null_op {
NullOp::SizeOf => layout.size.bytes(),
NullOp::AlignOf => layout.align.abi.bytes(),
- NullOp::Box => unreachable!(),
};
let val =
CValue::const_val(fx, fx.layout_of(fx.tcx.types.usize), val.into());

original_message: Remove NullOp::Box


message: Implement register allocation for inline assembly

diff:
use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_middle::mir::InlineAsmOperand;
+use rustc_span::Symbol;
use rustc_target::asm::*;
pub(crate) fn codegen_inline_asm<'tcx>(
@@ -115,11 +116,21 @@ pub(crate) fn codegen_inline_asm<'tcx>(
offset
};
+ let mut asm_gen = InlineAssemblyGenerator {
+ tcx: fx.tcx,
+ arch: InlineAsmArch::X86_64,
+ template,
+ operands,
+ options,
+ registers: Vec::new(),
+ };
+ asm_gen.allocate_registers();
+
// FIXME overlap input and output slots to save stack space
- for operand in operands {
+ for (i, operand) in operands.iter().enumerate() {
match *operand {
InlineAsmOperand::In { reg, ref value } => {
- let reg = expect_reg(reg);
+ let reg = asm_gen.registers[i].unwrap();
clobbered_regs.push((reg, new_slot(reg.reg_class())));
inputs.push((
reg,
@@ -128,7 +139,7 @@ pub(crate) fn codegen_inline_asm<'tcx>(
));
}
InlineAsmOperand::Out { reg, late: _, place } => {
- let reg = expect_reg(reg);
+ let reg = asm_gen.registers[i].unwrap();
clobbered_regs.push((reg, new_slot(reg.reg_class())));
if let Some(place) = place {
outputs.push((
@@ -139,7 +150,7 @@ pub(crate) fn codegen_inline_asm<'tcx>(
}
}
InlineAsmOperand::InOut { reg, late: _, ref in_value, out_place } => {
- let reg = expect_reg(reg);
+ let reg = asm_gen.registers[i].unwrap();
clobbered_regs.push((reg, new_slot(reg.reg_class())));
inputs.push((
reg,
@@ -164,25 +175,145 @@ pub(crate) fn codegen_inline_asm<'tcx>(
fx.inline_asm_index += 1;
let asm_name = format!("{}__inline_asm_{}", fx.symbol_name, inline_asm_index);
- let generated_asm = generate_asm_wrapper(
- &asm_name,
- InlineAsmArch::X86_64,
- options,
- template,
- clobbered_regs,
- &inputs,
- &outputs,
- );
+ let generated_asm = asm_gen.generate_asm_wrapper(&asm_name, clobbered_regs, &inputs, &outputs);
fx.cx.global_asm.push_str(&generated_asm);
call_inline_asm(fx, &asm_name, slot_size, inputs, outputs);
}
-fn generate_asm_wrapper(
- asm_name: &str,
+struct InlineAssemblyGenerator<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
arch: InlineAsmArch,
+ template: &'a [InlineAsmTemplatePiece],
+ operands: &'a [InlineAsmOperand<'tcx>],
options: InlineAsmOptions,
- template: &[InlineAsmTemplatePiece],
+ registers: Vec<Option<InlineAsmReg>>,
+}
+
+impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
+ fn allocate_registers(&mut self) {
+ let sess = self.tcx.sess;
+ let map = allocatable_registers(
+ self.arch,
+ |feature| sess.target_features.contains(&Symbol::intern(feature)),
+ &sess.target,
+ );
+ let mut allocated = FxHashMap::<_, (bool, bool)>::default();
+ let mut regs = vec![None; self.operands.len()];
+
+ // Add explicit registers to the allocated set.
+ for (i, operand) in self.operands.iter().enumerate() {
+ match *operand {
+ InlineAsmOperand::In { reg: InlineAsmRegOrRegClass::Reg(reg), .. } => {
+ regs[i] = Some(reg);
+ allocated.entry(reg).or_default().0 = true;
+ }
+ InlineAsmOperand::Out {
+ reg: InlineAsmRegOrRegClass::Reg(reg), late: true, ..
+ } => {
+ regs[i] = Some(reg);
+ allocated.entry(reg).or_default().1 = true;
+ }
+ InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(reg), .. }
+ | InlineAsmOperand::InOut { reg: InlineAsmRegOrRegClass::Reg(reg), .. } => {
+ regs[i] = Some(reg);
+ allocated.insert(reg, (true, true));
+ }
+ _ => (),
+ }
+ }
+
+ // Allocate out/inout/inlateout registers first because they are more constrained.
+ for (i, operand) in self.operands.iter().enumerate() {
+ match *operand {
+ InlineAsmOperand::Out {
+ reg: InlineAsmRegOrRegClass::RegClass(class),
+ late: false,
+ ..
+ }
+ | InlineAsmOperand::InOut {
+ reg: InlineAsmRegOrRegClass::RegClass(class), ..
+ } => {
+ let mut alloc_reg = None;
+ for &reg in &map[&class] {
+ let mut used = false;
+ reg.overlapping_regs(|r| {
+ if allocated.contains_key(&r) {
+ used = true;
+ }
+ });
+
+ if !used {
+ alloc_reg = Some(reg);
+ break;
+ }
+ }
+
+ let reg = alloc_reg.expect("cannot allocate registers");
+ regs[i] = Some(reg);
+ allocated.insert(reg, (true, true));
+ }
+ _ => (),
+ }
+ }
+
+ // Allocate in/lateout.
+ for (i, operand) in self.operands.iter().enumerate() {
+ match *operand {
+ InlineAsmOperand::In { reg: InlineAsmRegOrRegClass::RegClass(class), .. } => {
+ let mut alloc_reg = None;
+ for &reg in &map[&class] {
+ let mut used = false;
+ reg.overlapping_regs(|r| {
+ if allocated.get(&r).copied().unwrap_or_default().0 {
+ used = true;
+ }
+ });
+
+ if !used {
+ alloc_reg = Some(reg);
+ break;
+ }
+ }
+
+ let reg = alloc_reg.expect("cannot allocate registers");
+ regs[i] = Some(reg);
+ allocated.entry(reg).or_default().0 = true;
+ }
+ InlineAsmOperand::Out {
+ reg: InlineAsmRegOrRegClass::RegClass(class),
+ late: true,
+ ..
+ } => {
+ let mut alloc_reg = None;
+ for &reg in &map[&class] {
+ let mut used = false;
+ reg.overlapping_regs(|r| {
+ if allocated.get(&r).copied().unwrap_or_default().1 {
+ used = true;
+ }
+ });
+
+ if !used {
+ alloc_reg = Some(reg);
+ break;
+ }
+ }
+
+ let reg = alloc_reg.expect("cannot allocate registers");
+ regs[i] = Some(reg);
+ allocated.entry(reg).or_default().1 = true;
+ }
+ _ => (),
+ }
+ }
+
+ self.registers = regs;
+ }
+
+ fn generate_asm_wrapper(
+ &self,
+ asm_name: &str,
clobbered_regs: Vec<(InlineAsmReg, Size)>,
inputs: &[(InlineAsmReg, Size, Value)],
outputs: &[(InlineAsmReg, Size, CPlace<'_>)],
@@ -198,46 +329,51 @@ fn generate_asm_wrapper(
generated_asm.push_str(" mov rbp,rdi\n");
// Save clobbered registers
- if !options.contains(InlineAsmOptions::NORETURN) {
+ if !self.options.contains(InlineAsmOptions::NORETURN) {
// FIXME skip registers saved by the calling convention
for &(reg, offset) in &clobbered_regs {
- save_register(&mut generated_asm, arch, reg, offset);
+ save_register(&mut generated_asm, self.arch, reg, offset);
}
}
// Write input registers
for &(reg, offset, _value) in inputs {
- restore_register(&mut generated_asm, arch, reg, offset);
+ restore_register(&mut generated_asm, self.arch, reg, offset);
}
- if options.contains(InlineAsmOptions::ATT_SYNTAX) {
+ if self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
generated_asm.push_str(".att_syntax\n");
}
// The actual inline asm
- for piece in template {
+ for piece in self.template {
match piece {
InlineAsmTemplatePiece::String(s) => {
generated_asm.push_str(s);
}
- InlineAsmTemplatePiece::Placeholder { operand_idx: _, modifier: _, span: _ } => todo!(),
+ InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
+ self.registers[*operand_idx]
+ .unwrap()
+ .emit(&mut generated_asm, self.arch, *modifier)
+ .unwrap();
+ }
}
}
generated_asm.push('\n');
- if options.contains(InlineAsmOptions::ATT_SYNTAX) {
+ if self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
generated_asm.push_str(".intel_syntax noprefix\n");
}
- if !options.contains(InlineAsmOptions::NORETURN) {
+ if !self.options.contains(InlineAsmOptions::NORETURN) {
// Read output registers
for &(reg, offset, _place) in outputs {
- save_register(&mut generated_asm, arch, reg, offset);
+ save_register(&mut generated_asm, self.arch, reg, offset);
}
// Restore clobbered registers
for &(reg, offset) in clobbered_regs.iter().rev() {
- restore_register(&mut generated_asm, arch, reg, offset);
+ restore_register(&mut generated_asm, self.arch, reg, offset);
}
generated_asm.push_str(" pop rbp\n");
@@ -253,6 +389,7 @@ fn generate_asm_wrapper(
generated_asm
}
+}
fn call_inline_asm<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,


message: Remove expect_reg

diff:
@@ -42,8 +42,10 @@ pub(crate) fn codegen_inline_asm<'tcx>(
assert_eq!(operands.len(), 4);
let (leaf, eax_place) = match operands[1] {
InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
- let reg = expect_reg(reg);
- assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::ax));
+ assert_eq!(
+ reg,
+ InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax))
+ );
(
crate::base::codegen_operand(fx, in_value).load_scalar(fx),
crate::base::codegen_place(fx, out_place.unwrap()),
@@ -65,8 +67,10 @@ pub(crate) fn codegen_inline_asm<'tcx>(
};
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));
+ assert_eq!(
+ reg,
+ InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx))
+ );
(
crate::base::codegen_operand(fx, in_value).load_scalar(fx),
crate::base::codegen_place(fx, out_place.unwrap()),
@@ -76,8 +80,10 @@ pub(crate) fn codegen_inline_asm<'tcx>(
};
let edx_place = match operands[3] {
InlineAsmOperand::Out { reg, late: true, place } => {
- let reg = expect_reg(reg);
- assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::dx));
+ assert_eq!(
+ reg,
+ InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx))
+ );
crate::base::codegen_place(fx, place.unwrap())
}
_ => unreachable!(),
@@ -437,13 +443,6 @@ fn call_inline_asm<'tcx>(
}
}
-fn expect_reg(reg_or_class: InlineAsmRegOrRegClass) -> InlineAsmReg {
- match reg_or_class {
- InlineAsmRegOrRegClass::Reg(reg) => reg,
- InlineAsmRegOrRegClass::RegClass(class) => unimplemented!("{:?}", class),
- }
-}
-
fn save_register(generated_asm: &mut String, arch: InlineAsmArch, reg: InlineAsmReg, offset: Size) {
match arch {
InlineAsmArch::X86_64 => {

original_message: Remove expect_reg


message: Move stack slot allocation to a new fn

diff:
@@ -103,25 +103,10 @@ pub(crate) fn codegen_inline_asm<'tcx>(
crate::trap::trap_unimplemented(fx, "Alloca is not supported");
}
- let mut slot_size = Size::from_bytes(0);
let mut clobbered_regs = Vec::new();
let mut inputs = Vec::new();
let mut outputs = Vec::new();
- let mut new_slot = |reg_class: InlineAsmRegClass| {
- let reg_size = reg_class
- .supported_types(InlineAsmArch::X86_64)
- .iter()
- .map(|(ty, _)| ty.size())
- .max()
- .unwrap();
- let align = rustc_target::abi::Align::from_bytes(reg_size.bytes()).unwrap();
- slot_size = slot_size.align_to(align);
- let offset = slot_size;
- slot_size += reg_size;
- offset
- };
-
let mut asm_gen = InlineAssemblyGenerator {
tcx: fx.tcx,
arch: InlineAsmArch::X86_64,
@@ -129,44 +114,49 @@ pub(crate) fn codegen_inline_asm<'tcx>(
operands,
options,
registers: Vec::new(),
+ stack_slots_clobber: Vec::new(),
+ stack_slots_input: Vec::new(),
+ stack_slots_output: Vec::new(),
+ stack_slot_size: Size::from_bytes(0),
};
asm_gen.allocate_registers();
+ asm_gen.allocate_stack_slots();
// FIXME overlap input and output slots to save stack space
for (i, operand) in operands.iter().enumerate() {
match *operand {
InlineAsmOperand::In { reg, ref value } => {
let reg = asm_gen.registers[i].unwrap();
- clobbered_regs.push((reg, new_slot(reg.reg_class())));
+ clobbered_regs.push((reg, asm_gen.stack_slots_clobber[i].unwrap()));
inputs.push((
reg,
- new_slot(reg.reg_class()),
+ asm_gen.stack_slots_input[i].unwrap(),
crate::base::codegen_operand(fx, value).load_scalar(fx),
));
}
InlineAsmOperand::Out { reg, late: _, place } => {
let reg = asm_gen.registers[i].unwrap();
- clobbered_regs.push((reg, new_slot(reg.reg_class())));
+ clobbered_regs.push((reg, asm_gen.stack_slots_clobber[i].unwrap()));
if let Some(place) = place {
outputs.push((
reg,
- new_slot(reg.reg_class()),
+ asm_gen.stack_slots_output[i].unwrap(),
crate::base::codegen_place(fx, place),
));
}
}
InlineAsmOperand::InOut { reg, late: _, ref in_value, out_place } => {
let reg = asm_gen.registers[i].unwrap();
- clobbered_regs.push((reg, new_slot(reg.reg_class())));
+ clobbered_regs.push((reg, asm_gen.stack_slots_clobber[i].unwrap()));
inputs.push((
reg,
- new_slot(reg.reg_class()),
+ asm_gen.stack_slots_input[i].unwrap(),
crate::base::codegen_operand(fx, in_value).load_scalar(fx),
));
if let Some(out_place) = out_place {
outputs.push((
reg,
- new_slot(reg.reg_class()),
+ asm_gen.stack_slots_output[i].unwrap(),
crate::base::codegen_place(fx, out_place),
));
}
@@ -184,7 +174,7 @@ pub(crate) fn codegen_inline_asm<'tcx>(
let generated_asm = asm_gen.generate_asm_wrapper(&asm_name, clobbered_regs, &inputs, &outputs);
fx.cx.global_asm.push_str(&generated_asm);
- call_inline_asm(fx, &asm_name, slot_size, inputs, outputs);
+ call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs);
}
struct InlineAssemblyGenerator<'a, 'tcx> {
@@ -194,6 +184,10 @@ struct InlineAssemblyGenerator<'a, 'tcx> {
operands: &'a [InlineAsmOperand<'tcx>],
options: InlineAsmOptions,
registers: Vec<Option<InlineAsmReg>>,
+ stack_slots_clobber: Vec<Option<Size>>,
+ stack_slots_input: Vec<Option<Size>>,
+ stack_slots_output: Vec<Option<Size>>,
+ stack_slot_size: Size,
}
impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
@@ -317,6 +311,59 @@ fn allocate_registers(&mut self) {
self.registers = regs;
}
+ fn allocate_stack_slots(&mut self) {
+ let mut slot_size = Size::from_bytes(0);
+ let mut slots_clobber = vec![None; self.operands.len()];
+ let mut slots_input = vec![None; self.operands.len()];
+ let mut slots_output = vec![None; self.operands.len()];
+
+ let mut new_slot = |reg_class: InlineAsmRegClass| {
+ let reg_size = reg_class
+ .supported_types(InlineAsmArch::X86_64)
+ .iter()
+ .map(|(ty, _)| ty.size())
+ .max()
+ .unwrap();
+ let align = rustc_target::abi::Align::from_bytes(reg_size.bytes()).unwrap();
+ slot_size = slot_size.align_to(align);
+ let offset = slot_size;
+ slot_size += reg_size;
+ offset
+ };
+
+ // FIXME overlap input and output slots to save stack space
+ for (i, operand) in self.operands.iter().enumerate() {
+ match *operand {
+ InlineAsmOperand::In { reg, .. } => {
+ slots_clobber[i] = Some(new_slot(reg.reg_class()));
+ slots_input[i] = Some(new_slot(reg.reg_class()));
+ }
+ InlineAsmOperand::Out { reg, place, .. } => {
+ slots_clobber[i] = Some(new_slot(reg.reg_class()));
+ if place.is_some() {
+ slots_output[i] = Some(new_slot(reg.reg_class()));
+ }
+ }
+ InlineAsmOperand::InOut { reg, out_place, .. } => {
+ slots_clobber[i] = Some(new_slot(reg.reg_class()));
+ let slot = new_slot(reg.reg_class());
+ slots_input[i] = Some(slot);
+ if out_place.is_some() {
+ slots_output[i] = Some(slot);
+ }
+ }
+ InlineAsmOperand::Const { value: _ } => (),
+ InlineAsmOperand::SymFn { value: _ } => (),
+ InlineAsmOperand::SymStatic { def_id: _ } => (),
+ }
+ }
+
+ self.stack_slots_clobber = slots_clobber;
+ self.stack_slots_input = slots_input;
+ self.stack_slots_output = slots_output;
+ self.stack_slot_size = slot_size;
+ }
+
fn generate_asm_wrapper(
&self,
asm_name: &str,

original_message: Move stack slot allocation to a new fn


message: Code cleanup as a follow up to the previous commit

diff:
@@ -103,7 +103,6 @@ pub(crate) fn codegen_inline_asm<'tcx>(
crate::trap::trap_unimplemented(fx, "Alloca is not supported");
}
- let mut clobbered_regs = Vec::new();
let mut inputs = Vec::new();
let mut outputs = Vec::new();
@@ -122,40 +121,37 @@ pub(crate) fn codegen_inline_asm<'tcx>(
asm_gen.allocate_registers();
asm_gen.allocate_stack_slots();
+ let inline_asm_index = fx.inline_asm_index;
+ fx.inline_asm_index += 1;
+ let asm_name = format!("{}__inline_asm_{}", fx.symbol_name, inline_asm_index);
+
+ let generated_asm = asm_gen.generate_asm_wrapper(&asm_name);
+ fx.cx.global_asm.push_str(&generated_asm);
+
// FIXME overlap input and output slots to save stack space
for (i, operand) in operands.iter().enumerate() {
match *operand {
- InlineAsmOperand::In { reg, ref value } => {
- let reg = asm_gen.registers[i].unwrap();
- clobbered_regs.push((reg, asm_gen.stack_slots_clobber[i].unwrap()));
+ InlineAsmOperand::In { reg: _, ref value } => {
inputs.push((
- reg,
asm_gen.stack_slots_input[i].unwrap(),
crate::base::codegen_operand(fx, value).load_scalar(fx),
));
}
- InlineAsmOperand::Out { reg, late: _, place } => {
- let reg = asm_gen.registers[i].unwrap();
- clobbered_regs.push((reg, asm_gen.stack_slots_clobber[i].unwrap()));
+ InlineAsmOperand::Out { reg: _, late: _, place } => {
if let Some(place) = place {
outputs.push((
- reg,
asm_gen.stack_slots_output[i].unwrap(),
crate::base::codegen_place(fx, place),
));
}
}
- InlineAsmOperand::InOut { reg, late: _, ref in_value, out_place } => {
- let reg = asm_gen.registers[i].unwrap();
- clobbered_regs.push((reg, asm_gen.stack_slots_clobber[i].unwrap()));
+ InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
inputs.push((
- reg,
asm_gen.stack_slots_input[i].unwrap(),
crate::base::codegen_operand(fx, in_value).load_scalar(fx),
));
if let Some(out_place) = out_place {
outputs.push((
- reg,
asm_gen.stack_slots_output[i].unwrap(),
crate::base::codegen_place(fx, out_place),
));
@@ -167,13 +163,6 @@ 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.symbol_name, inline_asm_index);
-
- let generated_asm = asm_gen.generate_asm_wrapper(&asm_name, clobbered_regs, &inputs, &outputs);
- fx.cx.global_asm.push_str(&generated_asm);
-
call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs);
}
@@ -364,13 +353,7 @@ fn allocate_stack_slots(&mut self) {
self.stack_slot_size = slot_size;
}
- fn generate_asm_wrapper(
- &self,
- asm_name: &str,
- clobbered_regs: Vec<(InlineAsmReg, Size)>,
- inputs: &[(InlineAsmReg, Size, Value)],
- outputs: &[(InlineAsmReg, Size, CPlace<'_>)],
- ) -> String {
+ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
let mut generated_asm = String::new();
writeln!(generated_asm, ".globl {}", asm_name).unwrap();
writeln!(generated_asm, ".type {},@function", asm_name).unwrap();
@@ -384,14 +367,24 @@ fn generate_asm_wrapper(
// Save clobbered registers
if !self.options.contains(InlineAsmOptions::NORETURN) {
// FIXME skip registers saved by the calling convention
- for &(reg, offset) in &clobbered_regs {
- save_register(&mut generated_asm, self.arch, reg, offset);
+ for (reg, slot) in self
+ .registers
+ .iter()
+ .zip(self.stack_slots_clobber.iter().copied())
+ .filter_map(|(r, s)| r.zip(s))
+ {
+ save_register(&mut generated_asm, self.arch, reg, slot);
}
}
// Write input registers
- for &(reg, offset, _value) in inputs {
- restore_register(&mut generated_asm, self.arch, reg, offset);
+ for (reg, slot) in self
+ .registers
+ .iter()
+ .zip(self.stack_slots_input.iter().copied())
+ .filter_map(|(r, s)| r.zip(s))
+ {
+ restore_register(&mut generated_asm, self.arch, reg, slot);
}
if self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
@@ -414,19 +407,29 @@ fn generate_asm_wrapper(
}
generated_asm.push('\n');
- if self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
+ if is_x86 && self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
generated_asm.push_str(".intel_syntax noprefix\n");
}
if !self.options.contains(InlineAsmOptions::NORETURN) {
// Read output registers
- for &(reg, offset, _place) in outputs {
- save_register(&mut generated_asm, self.arch, reg, offset);
+ for (reg, slot) in self
+ .registers
+ .iter()
+ .zip(self.stack_slots_output.iter().copied())
+ .filter_map(|(r, s)| r.zip(s))
+ {
+ save_register(&mut generated_asm, self.arch, reg, slot);
}
// Restore clobbered registers
- for &(reg, offset) in clobbered_regs.iter().rev() {
- restore_register(&mut generated_asm, self.arch, reg, offset);
+ for (reg, slot) in self
+ .registers
+ .iter()
+ .zip(self.stack_slots_clobber.iter().copied())
+ .filter_map(|(r, s)| r.zip(s))
+ {
+ restore_register(&mut generated_asm, self.arch, reg, slot);
}
generated_asm.push_str(" pop rbp\n");
@@ -448,8 +451,8 @@ fn call_inline_asm<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
asm_name: &str,
slot_size: Size,
- inputs: Vec<(InlineAsmReg, Size, Value)>,
- outputs: Vec<(InlineAsmReg, Size, CPlace<'tcx>)>,
+ inputs: Vec<(Size, Value)>,
+ outputs: Vec<(Size, CPlace<'tcx>)>,
) {
let stack_slot = fx.bcx.func.create_stack_slot(StackSlotData {
kind: StackSlotKind::ExplicitSlot,
@@ -476,14 +479,14 @@ fn call_inline_asm<'tcx>(
fx.add_comment(inline_asm_func, asm_name);
}
- for (_reg, offset, value) in inputs {
+ for (offset, value) in inputs {
fx.bcx.ins().stack_store(value, stack_slot, i32::try_from(offset.bytes()).unwrap());
}
let stack_slot_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0);
fx.bcx.ins().call(inline_asm_func, &[stack_slot_addr]);
- for (_reg, offset, place) in outputs {
+ for (offset, place) in outputs {
let ty = fx.clif_type(place.layout().ty).unwrap();
let value = fx.bcx.ins().stack_load(ty, stack_slot, i32::try_from(offset.bytes()).unwrap());
place.write_cvalue(fx, CValue::by_val(value, place.layout()));


message: Skip registers saved by calling convention

diff:
@@ -320,21 +320,44 @@ fn allocate_stack_slots(&mut self) {
offset
};
+ // Allocate stack slots for saving clobbered registers
+ let abi_clobber =
+ InlineAsmClobberAbi::parse(self.arch, &self.tcx.sess.target, Symbol::intern("C"))
+ .unwrap()
+ .clobbered_regs();
+ for (i, reg) in self.registers.iter().enumerate().filter_map(|(i, r)| r.map(|r| (i, r))) {
+ let mut need_save = true;
+ // If the register overlaps with a register clobbered by function call, then
+ // we don't need to save it.
+ for r in abi_clobber {
+ r.overlapping_regs(|r| {
+ if r == reg {
+ need_save = false;
+ }
+ });
+
+ if !need_save {
+ break;
+ }
+ }
+
+ if need_save {
+ slots_clobber[i] = Some(new_slot(reg.reg_class()));
+ }
+ }
+
// FIXME overlap input and output slots to save stack space
for (i, operand) in self.operands.iter().enumerate() {
match *operand {
InlineAsmOperand::In { reg, .. } => {
- slots_clobber[i] = Some(new_slot(reg.reg_class()));
slots_input[i] = Some(new_slot(reg.reg_class()));
}
InlineAsmOperand::Out { reg, place, .. } => {
- slots_clobber[i] = Some(new_slot(reg.reg_class()));
if place.is_some() {
slots_output[i] = Some(new_slot(reg.reg_class()));
}
}
InlineAsmOperand::InOut { reg, out_place, .. } => {
- slots_clobber[i] = Some(new_slot(reg.reg_class()));
let slot = new_slot(reg.reg_class());
slots_input[i] = Some(slot);
if out_place.is_some() {
@@ -366,7 +389,6 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
// Save clobbered registers
if !self.options.contains(InlineAsmOptions::NORETURN) {
- // FIXME skip registers saved by the calling convention
for (reg, slot) in self
.registers
.iter()


message: Overlap input and output stack slots

diff:
@@ -128,7 +128,6 @@ pub(crate) fn codegen_inline_asm<'tcx>(
let generated_asm = asm_gen.generate_asm_wrapper(&asm_name);
fx.cx.global_asm.push_str(&generated_asm);
- // FIXME overlap input and output slots to save stack space
for (i, operand) in operands.iter().enumerate() {
match *operand {
InlineAsmOperand::In { reg: _, ref value } => {
@@ -306,7 +305,7 @@ fn allocate_stack_slots(&mut self) {
let mut slots_input = vec![None; self.operands.len()];
let mut slots_output = vec![None; self.operands.len()];
- let mut new_slot = |reg_class: InlineAsmRegClass| {
+ let new_slot_fn = |slot_size: &mut Size, reg_class: InlineAsmRegClass| {
let reg_size = reg_class
.supported_types(InlineAsmArch::X86_64)
.iter()
@@ -314,11 +313,11 @@ fn allocate_stack_slots(&mut self) {
.max()
.unwrap();
let align = rustc_target::abi::Align::from_bytes(reg_size.bytes()).unwrap();
- slot_size = slot_size.align_to(align);
- let offset = slot_size;
- slot_size += reg_size;
+ let offset = slot_size.align_to(align);
+ *slot_size = offset + reg_size;
offset
};
+ let mut new_slot = |x| new_slot_fn(&mut slot_size, x);
// Allocate stack slots for saving clobbered registers
let abi_clobber =
@@ -346,30 +345,50 @@ fn allocate_stack_slots(&mut self) {
}
}
- // FIXME overlap input and output slots to save stack space
+ // Allocate stack slots for inout
for (i, operand) in self.operands.iter().enumerate() {
match *operand {
- InlineAsmOperand::In { reg, .. } => {
- slots_input[i] = Some(new_slot(reg.reg_class()));
- }
- InlineAsmOperand::Out { reg, place, .. } => {
- if place.is_some() {
- slots_output[i] = Some(new_slot(reg.reg_class()));
- }
- }
- InlineAsmOperand::InOut { reg, out_place, .. } => {
+ InlineAsmOperand::InOut { reg, out_place: Some(_), .. } => {
let slot = new_slot(reg.reg_class());
slots_input[i] = Some(slot);
- if out_place.is_some() {
slots_output[i] = Some(slot);
}
+ _ => (),
+ }
+ }
+
+ let slot_size_before_input = slot_size;
+ let mut new_slot = |x| new_slot_fn(&mut slot_size, x);
+
+ // Allocate stack slots for input
+ for (i, operand) in self.operands.iter().enumerate() {
+ match *operand {
+ InlineAsmOperand::In { reg, .. }
+ | InlineAsmOperand::InOut { reg, out_place: None, .. } => {
+ slots_input[i] = Some(new_slot(reg.reg_class()));
+ }
+ _ => (),
+ }
+ }
+
+ // Reset slot size to before input so that input and output operands can overlap
+ // and save some memory.
+ let slot_size_after_input = slot_size;
+ slot_size = slot_size_before_input;
+ let mut new_slot = |x| new_slot_fn(&mut slot_size, x);
+
+ // Allocate stack slots for output
+ for (i, operand) in self.operands.iter().enumerate() {
+ match *operand {
+ InlineAsmOperand::Out { reg, place: Some(_), .. } => {
+ slots_output[i] = Some(new_slot(reg.reg_class()));
}
- InlineAsmOperand::Const { value: _ } => (),
- InlineAsmOperand::SymFn { value: _ } => (),
- InlineAsmOperand::SymStatic { def_id: _ } => (),
+ _ => (),
}
}
+ slot_size = slot_size.max(slot_size_after_input);
+
self.stack_slots_clobber = slots_clobber;
self.stack_slots_input = slots_input;
self.stack_slots_output = slots_output;

original_message: Overlap input and output stack slots


message: Skeleton for multiple arch support

diff:
@@ -402,9 +402,12 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
writeln!(generated_asm, ".section .text.{},\"ax\",@progbits", asm_name).unwrap();
writeln!(generated_asm, "{}:", asm_name).unwrap();
+ let is_x86 = matches!(self.arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
+
+ if is_x86 {
generated_asm.push_str(".intel_syntax noprefix\n");
- generated_asm.push_str(" push rbp\n");
- generated_asm.push_str(" mov rbp,rdi\n");
+ }
+ Self::prologue(&mut generated_asm, self.arch);
// Save clobbered registers
if !self.options.contains(InlineAsmOptions::NORETURN) {
@@ -414,7 +417,7 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
.zip(self.stack_slots_clobber.iter().copied())
.filter_map(|(r, s)| r.zip(s))
{
- save_register(&mut generated_asm, self.arch, reg, slot);
+ Self::save_register(&mut generated_asm, self.arch, reg, slot);
}
}
@@ -425,10 +428,10 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
.zip(self.stack_slots_input.iter().copied())
.filter_map(|(r, s)| r.zip(s))
{
- restore_register(&mut generated_asm, self.arch, reg, slot);
+ Self::restore_register(&mut generated_asm, self.arch, reg, slot);
}
- if self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
+ if is_x86 && self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
generated_asm.push_str(".att_syntax\n");
}
@@ -460,7 +463,7 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
.zip(self.stack_slots_output.iter().copied())
.filter_map(|(r, s)| r.zip(s))
{
- save_register(&mut generated_asm, self.arch, reg, slot);
+ Self::save_register(&mut generated_asm, self.arch, reg, slot);
}
// Restore clobbered registers
@@ -470,22 +473,84 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
.zip(self.stack_slots_clobber.iter().copied())
.filter_map(|(r, s)| r.zip(s))
{
- restore_register(&mut generated_asm, self.arch, reg, slot);
+ Self::restore_register(&mut generated_asm, self.arch, reg, slot);
}
- generated_asm.push_str(" pop rbp\n");
- generated_asm.push_str(" ret\n");
+ Self::epilogue(&mut generated_asm, self.arch);
} else {
- generated_asm.push_str(" ud2\n");
+ Self::epilogue_noreturn(&mut generated_asm, self.arch);
}
+ if is_x86 {
generated_asm.push_str(".att_syntax\n");
+ }
writeln!(generated_asm, ".size {name}, .-{name}", name = asm_name).unwrap();
generated_asm.push_str(".text\n");
generated_asm.push_str("\n\n");
generated_asm
}
+
+ fn prologue(generated_asm: &mut String, arch: InlineAsmArch) {
+ match arch {
+ InlineAsmArch::X86_64 => {
+ generated_asm.push_str(" push rbp\n");
+ generated_asm.push_str(" mov rbp,rdi\n");
+ }
+ _ => unimplemented!("prologue for {:?}", arch),
+ }
+ }
+
+ fn epilogue(generated_asm: &mut String, arch: InlineAsmArch) {
+ match arch {
+ InlineAsmArch::X86_64 => {
+ generated_asm.push_str(" pop rbp\n");
+ generated_asm.push_str(" ret\n");
+ }
+ _ => unimplemented!("epilogue for {:?}", arch),
+ }
+ }
+
+ fn epilogue_noreturn(generated_asm: &mut String, arch: InlineAsmArch) {
+ match arch {
+ InlineAsmArch::X86_64 => {
+ generated_asm.push_str(" ud2\n");
+ }
+ _ => unimplemented!("epilogue_noreturn for {:?}", arch),
+ }
+ }
+
+ fn save_register(
+ generated_asm: &mut String,
+ arch: InlineAsmArch,
+ reg: InlineAsmReg,
+ offset: Size,
+ ) {
+ match arch {
+ InlineAsmArch::X86_64 => {
+ write!(generated_asm, " mov [rbp+0x{:x}], ", offset.bytes()).unwrap();
+ reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
+ generated_asm.push('\n');
+ }
+ _ => unimplemented!("save_register for {:?}", arch),
+ }
+ }
+
+ fn restore_register(
+ generated_asm: &mut String,
+ arch: InlineAsmArch,
+ reg: InlineAsmReg,
+ offset: Size,
+ ) {
+ match arch {
+ InlineAsmArch::X86_64 => {
+ generated_asm.push_str(" mov ");
+ reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
+ writeln!(generated_asm, ", [rbp+0x{:x}]", offset.bytes()).unwrap();
+ }
+ _ => unimplemented!("restore_register for {:?}", arch),
+ }
+ }
}
fn call_inline_asm<'tcx>(
@@ -533,30 +598,3 @@ fn call_inline_asm<'tcx>(
place.write_cvalue(fx, CValue::by_val(value, place.layout()));
}
}
-
-fn save_register(generated_asm: &mut String, arch: InlineAsmArch, reg: InlineAsmReg, offset: Size) {
- match arch {
- InlineAsmArch::X86_64 => {
- write!(generated_asm, " mov [rbp+0x{:x}], ", offset.bytes()).unwrap();
- reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
- generated_asm.push('\n');
- }
- _ => unimplemented!("save_register for {:?}", arch),
- }
-}
-
-fn restore_register(
- generated_asm: &mut String,
- arch: InlineAsmArch,
- reg: InlineAsmReg,
- offset: Size,
-) {
- match arch {
- InlineAsmArch::X86_64 => {
- generated_asm.push_str(" mov ");
- reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
- writeln!(generated_asm, ", [rbp+0x{:x}]", offset.bytes()).unwrap();
- }
- _ => unimplemented!("restore_register for {:?}", arch),
- }
-}

original_message: Skeleton for multiple arch support


message: x86 inline asm support

diff:
@@ -493,6 +493,10 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
fn prologue(generated_asm: &mut String, arch: InlineAsmArch) {
match arch {
+ InlineAsmArch::X86 => {
+ generated_asm.push_str(" push ebp\n");
+ generated_asm.push_str(" mov ebp,[esp+8]\n");
+ }
InlineAsmArch::X86_64 => {
generated_asm.push_str(" push rbp\n");
generated_asm.push_str(" mov rbp,rdi\n");
@@ -503,6 +507,10 @@ fn prologue(generated_asm: &mut String, arch: InlineAsmArch) {
fn epilogue(generated_asm: &mut String, arch: InlineAsmArch) {
match arch {
+ InlineAsmArch::X86 => {
+ generated_asm.push_str(" pop ebp\n");
+ generated_asm.push_str(" ret\n");
+ }
InlineAsmArch::X86_64 => {
generated_asm.push_str(" pop rbp\n");
generated_asm.push_str(" ret\n");
@@ -513,7 +521,7 @@ fn epilogue(generated_asm: &mut String, arch: InlineAsmArch) {
fn epilogue_noreturn(generated_asm: &mut String, arch: InlineAsmArch) {
match arch {
- InlineAsmArch::X86_64 => {
+ InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
generated_asm.push_str(" ud2\n");
}
_ => unimplemented!("epilogue_noreturn for {:?}", arch),
@@ -527,6 +535,11 @@ fn save_register(
offset: Size,
) {
match arch {
+ InlineAsmArch::X86 => {
+ write!(generated_asm, " mov [ebp+0x{:x}], ", offset.bytes()).unwrap();
+ reg.emit(generated_asm, InlineAsmArch::X86, None).unwrap();
+ generated_asm.push('\n');
+ }
InlineAsmArch::X86_64 => {
write!(generated_asm, " mov [rbp+0x{:x}], ", offset.bytes()).unwrap();
reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
@@ -543,6 +556,11 @@ fn restore_register(
offset: Size,
) {
match arch {
+ InlineAsmArch::X86 => {
+ generated_asm.push_str(" mov ");
+ reg.emit(generated_asm, InlineAsmArch::X86, None).unwrap();
+ writeln!(generated_asm, ", [ebp+0x{:x}]", offset.bytes()).unwrap();
+ }
InlineAsmArch::X86_64 => {
generated_asm.push_str(" mov ");
reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();

original_message: x86 inline asm support


message: Dispatch inline asm to the correct arch

diff:
@@ -108,7 +108,7 @@ pub(crate) fn codegen_inline_asm<'tcx>(
let mut asm_gen = InlineAssemblyGenerator {
tcx: fx.tcx,
- arch: InlineAsmArch::X86_64,
+ arch: fx.tcx.sess.asm_arch.unwrap(),
template,
operands,
options,
@@ -306,12 +306,8 @@ fn allocate_stack_slots(&mut self) {
let mut slots_output = vec![None; self.operands.len()];
let new_slot_fn = |slot_size: &mut Size, reg_class: InlineAsmRegClass| {
- let reg_size = reg_class
- .supported_types(InlineAsmArch::X86_64)
- .iter()
- .map(|(ty, _)| ty.size())
- .max()
- .unwrap();
+ let reg_size =
+ reg_class.supported_types(self.arch).iter().map(|(ty, _)| ty.size()).max().unwrap();
let align = rustc_target::abi::Align::from_bytes(reg_size.bytes()).unwrap();
let offset = slot_size.align_to(align);
*slot_size = offset + reg_size;

original_message: Dispatch inline asm to the correct arch


message: Add RISC-V inline asm support

diff:
@@ -497,6 +497,18 @@ fn prologue(generated_asm: &mut String, arch: InlineAsmArch) {
generated_asm.push_str(" push rbp\n");
generated_asm.push_str(" mov rbp,rdi\n");
}
+ InlineAsmArch::RiscV32 => {
+ generated_asm.push_str(" addi sp, sp, -8\n");
+ generated_asm.push_str(" sw ra, 4(sp)\n");
+ generated_asm.push_str(" sw s0, 0(sp)\n");
+ generated_asm.push_str(" mv s0, a0\n");
+ }
+ InlineAsmArch::RiscV64 => {
+ generated_asm.push_str(" addi sp, sp, -16\n");
+ generated_asm.push_str(" sd ra, 8(sp)\n");
+ generated_asm.push_str(" sd s0, 0(sp)\n");
+ generated_asm.push_str(" mv s0, a0\n");
+ }
_ => unimplemented!("prologue for {:?}", arch),
}
}
@@ -511,6 +523,18 @@ fn epilogue(generated_asm: &mut String, arch: InlineAsmArch) {
generated_asm.push_str(" pop rbp\n");
generated_asm.push_str(" ret\n");
}
+ InlineAsmArch::RiscV32 => {
+ generated_asm.push_str(" lw s0, 0(sp)\n");
+ generated_asm.push_str(" lw ra, 4(sp)\n");
+ generated_asm.push_str(" addi sp, sp, 8\n");
+ generated_asm.push_str(" ret\n");
+ }
+ InlineAsmArch::RiscV64 => {
+ generated_asm.push_str(" ld s0, 0(sp)\n");
+ generated_asm.push_str(" ld ra, 8(sp)\n");
+ generated_asm.push_str(" addi sp, sp, 16\n");
+ generated_asm.push_str(" ret\n");
+ }
_ => unimplemented!("epilogue for {:?}", arch),
}
}
@@ -520,6 +544,9 @@ fn epilogue_noreturn(generated_asm: &mut String, arch: InlineAsmArch) {
InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
generated_asm.push_str(" ud2\n");
}
+ InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
+ generated_asm.push_str(" ebreak\n");
+ }
_ => unimplemented!("epilogue_noreturn for {:?}", arch),
}
}
@@ -541,6 +568,16 @@ fn save_register(
reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
generated_asm.push('\n');
}
+ InlineAsmArch::RiscV32 => {
+ generated_asm.push_str(" sw ");
+ reg.emit(generated_asm, InlineAsmArch::RiscV32, None).unwrap();
+ writeln!(generated_asm, ", 0x{:x}(s0)", offset.bytes()).unwrap();
+ }
+ InlineAsmArch::RiscV64 => {
+ generated_asm.push_str(" sd ");
+ reg.emit(generated_asm, InlineAsmArch::RiscV64, None).unwrap();
+ writeln!(generated_asm, ", 0x{:x}(s0)", offset.bytes()).unwrap();
+ }
_ => unimplemented!("save_register for {:?}", arch),
}
}
@@ -562,6 +599,16 @@ fn restore_register(
reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
writeln!(generated_asm, ", [rbp+0x{:x}]", offset.bytes()).unwrap();
}
+ InlineAsmArch::RiscV32 => {
+ generated_asm.push_str(" lw ");
+ reg.emit(generated_asm, InlineAsmArch::RiscV32, None).unwrap();
+ writeln!(generated_asm, ", 0x{:x}(s0)", offset.bytes()).unwrap();
+ }
+ InlineAsmArch::RiscV64 => {
+ generated_asm.push_str(" ld ");
+ reg.emit(generated_asm, InlineAsmArch::RiscV64, None).unwrap();
+ writeln!(generated_asm, ", 0x{:x}(s0)", offset.bytes()).unwrap();
+ }
_ => unimplemented!("restore_register for {:?}", arch),
}
}

original_message: Add RISC-V inline asm support


message: Fix allocated reg in AT&T style asm

diff:
@@ -438,6 +438,9 @@ fn generate_asm_wrapper(&self, asm_name: &str) -> String {
generated_asm.push_str(s);
}
InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
+ if self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
+ generated_asm.push('%');
+ }
self.registers[*operand_idx]
.unwrap()
.emit(&mut generated_asm, self.arch, *modifier)

original_message: Fix allocated reg in AT&T style asm


message:
Fix clean build from VS
Resolves
The Packages project needs to build after its dependencies
WebJobs.Script
WebJobs.Script.Extensibility
WebJobs.Script.WebHost
This fix explicitly adds these dependencies so VS will build the projects in the right order.

diff:
@@ -17,6 +17,11 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebJobs.Script.Tests", "test\WebJobs.Script.Tests\WebJobs.Script.Tests.csproj", "{38C609D3-B126-43B0-B9D8-6D62FAEE2ABC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Packages", "src\Packages\Packages.csproj", "{34AB8F63-18DE-4E0D-B21C-15E33B091634}"
+ ProjectSection(ProjectDependencies) = postProject
+ {479B0873-CBDC-4AE9-85BA-DD6CF2DCA29D} = {479B0873-CBDC-4AE9-85BA-DD6CF2DCA29D}
+ {1DC670CD-F42F-4D8F-97BD-0E1AA8221094} = {1DC670CD-F42F-4D8F-97BD-0E1AA8221094}
+ {63C9B2F4-F122-4ACC-968F-9B8F0880A72F} = {63C9B2F4-F122-4ACC-968F-9B8F0880A72F}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebJobs.Script.Host", "src\WebJobs.Script.Host\WebJobs.Script.Host.csproj", "{F5FAB5A1-A816-441B-86BF-CA3CAE2F7588}"
EndProject

license: MIT License

repo: azure/azure-functions-host

original_message:
Fix clean build from VS

Resolves #383

The Packages project needs to build after its dependencies
- WebJobs.Script
- WebJobs.Script.Extensibility
- WebJobs.Script.WebHost

This fix explicitly adds these dependencies so VS will build the projects in the right order.


message: Set minthreads on webhost start

diff:
using System;
using System.Linq;
using System.Net;
+using System.Threading;
using System.Web.Http;
using Microsoft.Azure.WebJobs.Script.Config;
using Microsoft.Azure.WebJobs.Script.Diagnostics;
@@ -23,6 +24,7 @@ protected void Application_Start()
ServicePointManager.DefaultConnectionLimit = ScriptConstants.DynamicSkuConnectionLimit;
}
+ ConfigureMinimumThreads(settingsManager.IsDynamicSku);
VerifyAndEnableShadowCopy(webHostSettings);
using (var metricsLogger = new WebHostMetricsLogger())
@@ -45,6 +47,25 @@ private static void VerifyAndEnableShadowCopy(WebHostSettings webHostSettings)
}
}
+ private static void ConfigureMinimumThreads(bool isDynamicSku)
+ {
+ // For information on MinThreads, see:
+ // https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool.setminthreads?view=netframework-4.7
+ // https://docs.microsoft.com/en-us/azure/redis-cache/cache-faq#important-details-about-threadpool-growth
+ // https://blogs.msdn.microsoft.com/perfworld/2010/01/13/how-can-i-improve-the-performance-of-asp-net-by-adjusting-the-clr-thread-throttling-properties/
+ //
+ // This behavior can be overridden by using the "ComPlus_ThreadPool_ForceMinWorkerThreads" environment variable (honored by the .NET threadpool).
+
+ // The dynamic plan has some limits that mean that a given instance is using effectively a single core, so we should not use Environment.Processor count in this case.
+ var effectiveCores = isDynamicSku ? 1 : Environment.ProcessorCount;
+
+ // This value was derived by looking at the thread count for several function apps running load on a multicore machine and dividing by the number of cores.
+ const int minThreadsPerLogicalProcessor = 6;
+
+ int minThreadCount = effectiveCores * minThreadsPerLogicalProcessor;
+ ThreadPool.SetMinThreads(minThreadCount, minThreadCount);
+ }
+
internal static string GetShadowCopyPath(string currentShadowCopyDirectories, string scriptPath)
{
return string.Join(";", new[] { currentShadowCopyDirectories, scriptPath }.Where(s => !string.IsNullOrEmpty(s)));

original_message: Set minthreads on webhost start


message: Suppress host restart for race condition case where hostingstart.html change evaluates to a host restart

diff:
@@ -1624,6 +1624,16 @@ private void OnFileChanged(FileSystemEventArgs e)
string.Compare(fileName, ScriptConstants.ProxyMetadataFileName, StringComparison.OrdinalIgnoreCase) == 0) ||
!_directorySnapshot.SequenceEqual(Directory.EnumerateDirectories(ScriptConfig.RootScriptPath)))
{
+ // CRI ICM: 46695121
+ // Do not allow the host to restart if this notification is for the hostingstart.html file in the root
+ if (string.Compare(directory, "hostingstart.html", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ string hostingWarning = string.Format(CultureInfo.InvariantCulture, "Unexpected file change event detected for '{0}' which evaluated to a host restart. Suppressing restart.", e.FullPath);
+ TraceWriter.Warning(hostingWarning);
+ Logger?.LogWarning(hostingWarning);
+ return;
+ }
+
bool shutdown = false;
string fileExtension = Path.GetExtension(fileName);
if (!string.IsNullOrEmpty(fileExtension) && ScriptConstants.AssemblyFileTypes.Contains(fileExtension, StringComparer.OrdinalIgnoreCase))


message: Do not send any content if root is pinged by scale controller

diff:
@@ -52,7 +52,7 @@ public override async Task<HttpResponseMessage> ExecuteAsync(HttpControllerConte
{
// if the request is to the root and we can't find any matching FunctionDescriptors which might have been setup by proxies
// then homepage logic will be applied.
- return IsHomepageDisabled
+ return (IsHomepageDisabled || request.IsAntaresInternalRequest())
? new HttpResponseMessage(HttpStatusCode.NoContent)
: new HttpResponseMessage(HttpStatusCode.OK)
{

diff:
@@ -210,6 +210,22 @@ public async Task Home_Get_WithHomepageDisabled_Succeeds()
}
}
+ [Fact]
+ public async Task Home_Get_InAzureEnvironment_AsInternalRequest_ReturnsNoContent()
+ {
+ // Pings to the site root should not return the homepage content if they are internal requests.
+ // This test sets a website instance Id which means that we'll go down the IsAzureEnvironment = true codepath
+ // but the sent request does NOT include an X-ARR-LOG-ID header. This indicates the request was internal.
+
+ using (new TestScopedSettings(_settingsManager, EnvironmentSettingNames.AzureWebsiteInstanceId, "123"))
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, string.Empty);
+
+ HttpResponseMessage response = await this._fixture.HttpClient.SendAsync(request);
+ Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
+ }
+ }
+
[Fact]
public async Task HttpTrigger_CSharp_Poco_Post_Succeeds()
{


diff:
@@ -114,7 +114,7 @@ private void ProcessLeaseTimerTick(object state)
{
t.Exception.Handle(e =>
{
- ProcessLeaseError(e.Message);
+ ProcessLeaseError(Utility.FlattenException(e));
return true;
});
}

original_message: Better error message on PrimaryHostCoordinator (#2531)


message: Fix - make lease timeout range check more robust

diff:
@@ -89,9 +89,9 @@ internal IDistributedLock LockHandle
ILoggerFactory loggerFactory,
TimeSpan? renewalInterval = null)
{
- if (leaseTimeout.TotalSeconds < 15 || leaseTimeout.TotalSeconds > 60)
+ if (leaseTimeout < TimeSpan.FromSeconds(15) || leaseTimeout > TimeSpan.FromSeconds(60))
{
- throw new ArgumentOutOfRangeException(nameof(leaseTimeout), $"The {nameof(leaseTimeout)} should be between 15 and 60 seconds");
+ throw new ArgumentOutOfRangeException(nameof(leaseTimeout), $"The {nameof(leaseTimeout)} should be between 15 and 60 seconds but was '{leaseTimeout}'");
}
var manager = new PrimaryHostCoordinator(lockManager, leaseTimeout, hostId, instanceId, loggerFactory, renewalInterval);

diff:
@@ -37,6 +37,20 @@ private static IDistributedLockManager CreateLockManager()
return lockManager;
}
+ [Theory]
+ [InlineData(14.99)]
+ [InlineData(60.01)]
+ public void RejectsInvalidLeaseTimeout(double leaseTimeoutSeconds)
+ {
+ var leaseTimeout = TimeSpan.FromSeconds(leaseTimeoutSeconds);
+
+ string hostId = Guid.NewGuid().ToString();
+ string instanceId = Guid.NewGuid().ToString();
+
+ Assert.Throws<ArgumentOutOfRangeException>(
+ () => PrimaryHostCoordinator.Create(CreateLockManager(), leaseTimeout, hostId, instanceId, _loggerFactory));
+ }
+
[Fact]
public async Task HasLease_WhenLeaseIsAcquired_ReturnsTrue()
{

original_message: Fix #2363 - make lease timeout range check more robust


message: Reduce chance of sliding window test flaking out

diff:
@@ -15,23 +15,35 @@ public class SlidingWindowTests
[Fact]
public async Task GetEvents_RemovesExpiredItems()
{
- var window = new SlidingWindow<MyItem>(TimeSpan.FromSeconds(1));
+ var expiry = TimeSpan.FromSeconds(1);
+ var window = new SlidingWindow<MyItem>(expiry);
+ var initialEventCount = 5;
+ var insertionTimestamps = new DateTime[initialEventCount];
- for (int i = 0; i < 5; i++)
+ for (int i = 0; i < initialEventCount; i++)
{
window.AddEvent(new MyItem { Data = i });
+ insertionTimestamps[i] = DateTime.Now;
await Task.Delay(100);
}
var evts = window.GetEvents().ToArray();
- Assert.Equal(5, evts.Length);
- for (int i = 0; i < 5; i++)
+
+ // How many items have expired? We're at the mercy of the task scheduler here.
+ // In theory we should have only waited 5 * 100 = 500ms and have zero expired items but we might have waited longer.
+ var insertionCompleteTimestamp = DateTime.Now;
+ var expiredCount = insertionTimestamps.Count(t => (insertionCompleteTimestamp - t) > expiry);
+ var expectedCount = initialEventCount - expiredCount;
+
+ // Check the total count and each of the non expired items
+ Assert.Equal(expectedCount, evts.Length);
+ for (int i = 0; i < expectedCount; i++)
{
- Assert.Equal(i, evts[i].Data);
+ Assert.Equal(i + expiredCount, evts[i].Data);
}
// now let the items expire
- await Task.Delay(1000);
+ await Task.Delay(expiry);
// add a new event that shouldn't be expired
var evt = new MyItem { Data = 7 };


message: Fix flaky timer flush test

diff:
@@ -375,7 +375,7 @@ public async Task TimerFlush_MultipleEventsQueued_EmitsExpectedEvents()
}
[Fact]
- public void TimerFlush_CalledOnExpectedInterval()
+ public async Task TimerFlush_CalledOnExpectedInterval()
{
int flushInterval = 10;
Mock<IEventGenerator> mockGenerator = new Mock<IEventGenerator>();
@@ -389,11 +389,8 @@ public void TimerFlush_CalledOnExpectedInterval()
numFlushes++;
});
- Thread.Sleep(200);
-
- // here we're just verifying that we're called
- // multiple times
- Assert.True(numFlushes >= 5, $"Expected numFlushes >= 5; Actual: {numFlushes}");
+ // here we're just verifying that we're called multiple times
+ await TestHelpers.Await(() => numFlushes >= 5, timeout: 2000, pollingInterval: 100, userMessageCallback: () => $"Expected numFlushes >= 5; Actual: {numFlushes}");
mockEventManager.VerifyAll();
}

original_message: Fix flaky timer flush test


message:
Fix flaky backoff tests
Removes potential rounding errors by comparing two timespans

diff:
@@ -59,7 +59,7 @@ public async Task DelayWithBackoffAsync_Returns_WhenCancelled()
sw.Start();
await Utility.DelayWithBackoffAsync(20, tokenSource.Token);
sw.Stop();
- Assert.True(sw.ElapsedMilliseconds < 1000, $"Expected sw.ElapsedMilliseconds < 1000; Actual: {sw.ElapsedMilliseconds}");
+ Assert.True(sw.Elapsed < TimeSpan.FromSeconds(2), $"Expected sw.Elapsed < TimeSpan.FromSeconds(2); Actual: {sw.Elapsed.TotalMilliseconds}");
}
[Fact]
@@ -69,7 +69,7 @@ public async Task DelayWithBackoffAsync_DelaysAsExpected()
sw.Start();
await Utility.DelayWithBackoffAsync(2, CancellationToken.None);
sw.Stop();
- Assert.True(sw.ElapsedMilliseconds >= 2000, $"Expected sw.ElapsedMilliseconds >= 2000; Actual: {sw.ElapsedMilliseconds}");
+ Assert.True(sw.Elapsed >= TimeSpan.FromSeconds(2), $"Expected sw.Elapsed >= TimeSpan.FromSeconds(2); Actual: {sw.Elapsed.TotalMilliseconds}");
}
[Theory]

original_message:
Fix flaky backoff tests
- Removes potential rounding errors by comparing two timespans


message: increasing node version

diff:
@@ -43,7 +43,7 @@ for:
- ps: >-
$env:CommitHash = "$env:APPVEYOR_REPO_COMMIT"
- Install-Product node 8.4.0 x86
+ Install-Product node 10.0.0 x86
build_script:
- ps: |
$hasTag = Test-Path env:APPVEYOR_REPO_TAG_NAME

original_message: increasing node version (#3019)


message: Remove mention of webjobs dashboard from readme

diff:
@@ -11,7 +11,7 @@ WebJobs.Script
This repo contains libraries that enable a **light-weight scripting model** for the [Azure WebJobs SDK](http://github.com/Azure/azure-webjobs-sdk). You simply provide job function **scripts** written in various languages (e.g. Javascript/[Node.js](http://nodejs.org), C#, Python, F#, PowerShell, PHP, CMD, BAT, BASH scripts, etc.) along with a simple **function.json** metadata file that indicates how those functions should be invoked, and the scripting library does the work necessary to plug those scripts into the [Azure WebJobs SDK](https://github.com/Azure/azure-webjobs-sdk) runtime.
-These libraries are the runtime used by [Azure Functions](https://azure.microsoft.com/en-us/services/functions/). The runtime builds upon the tried and true [Azure WebJobs SDK](https://github.com/Azure/azure-webjobs-sdk) - this library just layers on top to allow you to "**script the WebJobs SDK**". So you get the full benefits and the power of the WebJobs SDK, including the [WebJobs Dashboard](http://azure.microsoft.com/en-us/documentation/videos/azure-webjobs-dashboard-site-extension/).
+These libraries are the runtime used by [Azure Functions](https://azure.microsoft.com/en-us/services/functions/). The runtime builds upon the tried and true [Azure WebJobs SDK](https://github.com/Azure/azure-webjobs-sdk) - this library just layers on top to allow you to "**script the WebJobs SDK**".
**An important note on language support levels** - while many languages are supported, their level of support differs in important ways, making some languages more suitable than others for certain workloads. The **first class** languages are C#, F# and Javascript/Node.js. Functions written in these languages are run **in process** and are suitable for any workload. The remaining languages are considered **experimental**. Functions written in these languages are scripts that are run **out of process**. While there are many scenarios where this is acceptable, it won't be acceptable for high load scenarios where the overhead of a new process for each invocation won't scale.


message: Fix flaky backoff test

diff:
@@ -69,7 +69,10 @@ public async Task DelayWithBackoffAsync_DelaysAsExpected()
sw.Start();
await Utility.DelayWithBackoffAsync(2, CancellationToken.None);
sw.Stop();
- Assert.True(sw.Elapsed >= TimeSpan.FromSeconds(2), $"Expected sw.Elapsed >= TimeSpan.FromSeconds(2); Actual: {sw.Elapsed.TotalMilliseconds}");
+
+ // Workaround annoying test failures such as "Expected sw.Elapsed >= TimeSpan.FromSeconds(2); Actual: 1999.4092" by waiting slightly less than 2 seconds
+ // Not sure what causes it, but either Task.Delay sometimes doesn't wait quite long enough or there is some clock skew.
+ Assert.True(sw.Elapsed >= TimeSpan.FromMilliseconds(1990), $"Expected sw.Elapsed >= TimeSpan.FromMilliseconds(1990); Actual: {sw.Elapsed.TotalMilliseconds}");
}
[Theory]

original_message: Fix flaky backoff test


message:
Fix test flake for FunctionLogging_Succeeds
The call to GetAllLogMessages sometimes ends up enumerating the loggers while they are modified. This should resolve.

diff:
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Azure.WebJobs.Script.Tests;
@@ -18,19 +19,13 @@ public TestLoggerProvider(Func<string, LogLevel, bool> filter = null)
_filter = filter;
}
- private Dictionary<string, TestLogger> LoggerCache { get; } = new Dictionary<string, TestLogger>();
+ private ConcurrentDictionary<string, TestLogger> LoggerCache { get; } = new ConcurrentDictionary<string, TestLogger>();
public IEnumerable<TestLogger> CreatedLoggers => LoggerCache.Values;
public ILogger CreateLogger(string categoryName)
{
- if (!LoggerCache.TryGetValue(categoryName, out TestLogger logger))
- {
- logger = new TestLogger(categoryName, _filter);
- LoggerCache.Add(categoryName, logger);
- }
-
- return logger;
+ return LoggerCache.GetOrAdd(categoryName, (key) => new TestLogger(key, _filter));
}
public IList<LogMessage> GetAllLogMessages() => CreatedLoggers.SelectMany(l => l.GetLogMessages()).OrderBy(p => p.Timestamp).ToList();


message:
Fix the private site extension builds
remove the unused one (i.e. the one that is not "no-runtime")
add a custom xdt for private that doesnt register a global module

diff:
@@ -180,8 +180,31 @@ function CreateZips([string] $runtimeSuffix) {
Remove-Item -Recurse -Force "$privateSiteExtensionPath\runtimes\osx" -ErrorAction SilentlyContinue
# Create site extension packages
+
+ # Prepare private "no-runtime" with custom xdt
+ if ($runtimeSuffix -eq ".no-runtime") {
+ $currentXdtPath = "$privateSiteExtensionPath\applicationHost.xdt"
+ $tempXdtDir = "$buildOutput\xdt-temp"
+ $tempPublicXdtPath = "$tempXdtDir\applicationHost-public.xdt"
+
+ # Make a temp location
+ New-Item -Itemtype directory -path $tempXdtDir -ErrorAction SilentlyContinue
+
+ # Move the current (public) xdt to the temp location
+ Move-Item $currentXdtPath $tempPublicXdtPath
+
+ # Drop in the private XDT
+ Copy-Item .\src\WebJobs.Script.WebHost\applicationHost-private.xdt $currentXdtPath
+
+ # Make the zip
ZipContent $publishTarget "$buildOutput\Functions.Private.$extensionVersion-alpha$runtimeSuffix.zip"
+ # Restore the public XDT
+ Move-Item $tempPublicXdtPath $currentXdtPath -Force
+
+ Remove-Item $tempXdtDir -Recurse
+ }
+
#Build site extension
Write-Host "privateSiteExtensionPath: " $privateSiteExtensionPath
Rename-Item "$privateSiteExtensionPath" "$siteExtensionPath\$extensionVersion-alpha"

original_message:
Fix the private site extension builds
- remove the unused one (i.e. the one that is not "no-runtime")
- add a custom xdt for private that doesnt register a global module


message: Prevent built in extensions from being declared in extensions.json

diff:
@@ -17,6 +17,9 @@ public class ExtensionsMetadataGenerator
{
private const string WebJobsStartupAttributeType = "Microsoft.Azure.WebJobs.Hosting.WebJobsStartupAttribute";
+ // These assemblies are always loaded by the functions runtime and should not be listed in extensions.json
+ private static readonly string[] ExcludedAssemblies = new[] { "Microsoft.Azure.WebJobs.Extensions.dll", "Microsoft.Azure.WebJobs.Extensions.Http.dll" };
+
public static void Generate(string sourcePath, string outputPath, Action<string> logger)
{
if (!Directory.Exists(sourcePath))
@@ -27,7 +30,7 @@ public static void Generate(string sourcePath, string outputPath, Action<string>
var extensionReferences = new List<ExtensionReference>();
var targetAssemblies = Directory.EnumerateFiles(sourcePath, "*.dll")
- .Where(f => !Path.GetFileName(f).StartsWith("System", StringComparison.OrdinalIgnoreCase));
+ .Where(f => !AssemblyShouldBeSkipped(Path.GetFileName(f)));
foreach (var path in targetAssemblies)
{
@@ -47,6 +50,8 @@ public static void Generate(string sourcePath, string outputPath, Action<string>
File.WriteAllText(outputPath, json);
}
+ public static bool AssemblyShouldBeSkipped(string fileName) => fileName.StartsWith("System", StringComparison.OrdinalIgnoreCase) || ExcludedAssemblies.Contains(fileName, StringComparer.OrdinalIgnoreCase);
+
public static string GenerateExtensionsJson(IEnumerable<ExtensionReference> extensionReferences)
{
var referenceObjects = extensionReferences.Select(r => string.Format("{2} {{ \"name\": \"{0}\", \"typeName\":\"{1}\"}}", r.Name, r.TypeName, Environment.NewLine));

diff:
@@ -36,5 +36,17 @@ public void GenerateExtensionsJson_Succeeds()
Assert.Equal("BarExtension", extensions[1]["name"]);
Assert.Equal(typeof(BarWebJobsStartup).AssemblyQualifiedName, extensions[1]["typeName"]);
}
+
+ [Theory]
+ [InlineData("System.Linq.dll", true)]
+ [InlineData("Microsoft.Azure.WebJobs.Extensions.dll", true)]
+ [InlineData("Microsoft.Azure.WebJobs.Extensions.Http.dll", true)]
+ [InlineData("microsoft.azure.webjobs.extensions.http.dll", true)]
+ [InlineData("Microsoft.Azure.EventGrid.dll", false)]
+ [InlineData("MyCoolExtension.dll", false)]
+ public void SomeAssembliesAreSkipped(string assemblyFileName, bool shouldSkip)
+ {
+ Assert.Equal(shouldSkip, ExtensionsMetadataGenerator.ExtensionsMetadataGenerator.AssemblyShouldBeSkipped(assemblyFileName));
+ }
}
}


message: Fix metadata generator so that extensions.json is included when publishing

diff:
ContinueOnError="true"/>
</Target>
+ <Target Name="_GenerateFunctionsExtensionsMetadataPostPublish"
+ AfterTargets="Publish">
+ <GenerateFunctionsExtensionsMetadata
+ SourcePath="$(PublishDir)bin"
+ OutputPath="$(PublishDir)bin"/>
+ </Target>
+
<Target Name="_ResolveAdditionalReferenceCopyLocalPathsForFunctionsExtensions"
AfterTargets="ResolveReferences"
Condition="$(_IsFunctionsSdkBuild) != 'true'">


message: Zip up the crossgen symbols so they appear as build artifacts

diff:
@@ -205,6 +205,12 @@ function CreateZips([string] $runtimeSuffix) {
Remove-Item $tempXdtDir -Recurse
}
+ # Zip up symbols for builds with runtime embedded
+ if ($runtimeSuffix -eq "") {
+ ZipContent "$buildOutput\publish.win-x86\Symbols" "$buildOutput\Functions.Symbols.$extensionVersion-alpha.win-x86.zip"
+ ZipContent "$buildOutput\publish.win-x64\Symbols" "$buildOutput\Functions.Symbols.$extensionVersion-alpha.win-x64.zip"
+ }
+
#Build site extension
Write-Host "privateSiteExtensionPath: " $privateSiteExtensionPath
Rename-Item "$privateSiteExtensionPath" "$siteExtensionPath\$extensionVersion-alpha"


message: update node worker to beta 4

diff:
</PackageReference>
<PackageReference Include="Microsoft.Azure.AppService.Proxy.Client" Version="2.0.5350001-beta-fc119b98" />
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.1.1-beta03" />
- <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta3-10073" />
+ <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta4-10081" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.0-beta8" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.0-beta8" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.0-beta8" />

diff:
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="1.9.1" />
<PackageReference Include="Microsoft.Azure.EventHubs" Version="2.1.0" />
- <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta3-10073" />
+ <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta4-10081" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.0-beta8" />
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.1.1-beta03" />
<PackageReference Include="Microsoft.Azure.Mobile.Client" Version="4.0.2" />

original_message: update node worker to beta 4


message: fixing async funcs

diff:
</PackageReference>
<PackageReference Include="Microsoft.Azure.AppService.Proxy.Client" Version="2.0.5350001-beta-fc119b98" />
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.1.1-beta03" />
- <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta4-10081" />
+ <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta4-10082" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.0-beta8" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.0-beta8" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.0-beta8" />

diff:
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="1.9.1" />
<PackageReference Include="Microsoft.Azure.EventHubs" Version="2.1.0" />
- <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta4-10081" />
+ <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta4-10082" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.0-beta8" />
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.1.1-beta03" />
<PackageReference Include="Microsoft.Azure.Mobile.Client" Version="4.0.2" />

original_message: fixing async funcs


message: use beta6 node worker - fails better for low versions of node

diff:
</PackageReference>
<PackageReference Include="Microsoft.Azure.AppService.Proxy.Client" Version="2.0.5350001-beta-fc119b98" />
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.2.0-beta01-SNAPSHOT" />
- <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta4-10082" />
+ <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta6" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.2" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.0" />

diff:
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.1.2" />
<PackageReference Include="Microsoft.Azure.EventHubs" Version="2.1.0" />
- <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta4-10082" />
+ <PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.0.0-beta6" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.2" />
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.2.0-beta01-SNAPSHOT" />
<PackageReference Include="Microsoft.Azure.Mobile.Client" Version="4.0.2" />