diff --git a/cranelift/codegen/meta/src/cdsl/types.rs b/cranelift/codegen/meta/src/cdsl/types.rs index d971f45c61..830ae24666 100644 --- a/cranelift/codegen/meta/src/cdsl/types.rs +++ b/cranelift/codegen/meta/src/cdsl/types.rs @@ -407,6 +407,8 @@ impl fmt::Debug for VectorType { #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub(crate) enum SpecialType { Flag(shared_types::Flag), + // FIXME remove once the old style backends are removed. + StructArgument, } impl SpecialType { @@ -421,6 +423,9 @@ impl SpecialType { "CPU flags representing the result of a floating point comparison. These flags can be tested with a :type:`floatcc` condition code.", ), + SpecialType::StructArgument => { + String::from("After legalization sarg__ arguments will get this type.") + } } } @@ -428,6 +433,7 @@ impl SpecialType { pub fn lane_bits(self) -> u64 { match self { SpecialType::Flag(_) => 0, + SpecialType::StructArgument => 0, } } @@ -436,6 +442,7 @@ impl SpecialType { match self { SpecialType::Flag(shared_types::Flag::IFlags) => 1, SpecialType::Flag(shared_types::Flag::FFlags) => 2, + SpecialType::StructArgument => 3, } } } @@ -445,6 +452,7 @@ impl fmt::Display for SpecialType { match *self { SpecialType::Flag(shared_types::Flag::IFlags) => write!(f, "iflags"), SpecialType::Flag(shared_types::Flag::FFlags) => write!(f, "fflags"), + SpecialType::StructArgument => write!(f, "sarg__"), } } } @@ -456,6 +464,7 @@ impl fmt::Debug for SpecialType { "{}", match *self { SpecialType::Flag(_) => format!("FlagsType({})", self), + SpecialType::StructArgument => format!("StructArgument"), } ) } @@ -469,12 +478,14 @@ impl From for SpecialType { pub(crate) struct SpecialTypeIterator { flag_iter: shared_types::FlagIterator, + done: bool, } impl SpecialTypeIterator { fn new() -> Self { Self { flag_iter: shared_types::FlagIterator::new(), + done: false, } } } @@ -485,7 +496,12 @@ impl Iterator for SpecialTypeIterator { if let Some(f) = self.flag_iter.next() { Some(SpecialType::from(f)) } else { - None + if !self.done { + self.done = true; + Some(SpecialType::StructArgument) + } else { + None + } } } } diff --git a/cranelift/codegen/meta/src/isa/x86/encodings.rs b/cranelift/codegen/meta/src/isa/x86/encodings.rs index da04019a1b..09ec008d73 100644 --- a/cranelift/codegen/meta/src/isa/x86/encodings.rs +++ b/cranelift/codegen/meta/src/isa/x86/encodings.rs @@ -465,6 +465,7 @@ fn define_moves(e: &mut PerCpuModeEncodings, shared_defs: &SharedDefinitions, r: let sextend = shared.by_name("sextend"); let set_pinned_reg = shared.by_name("set_pinned_reg"); let uextend = shared.by_name("uextend"); + let dummy_sarg__ = shared.by_name("dummy_sarg__"); // Shorthands for recipes. let rec_copysp = r.template("copysp"); @@ -482,6 +483,7 @@ fn define_moves(e: &mut PerCpuModeEncodings, shared_defs: &SharedDefinitions, r: let rec_umr_reg_to_ssa = r.template("umr_reg_to_ssa"); let rec_urm_noflags = r.template("urm_noflags"); let rec_urm_noflags_abcd = r.template("urm_noflags_abcd"); + let rec_dummy_sarg__ = r.recipe("dummy_sarg__"); // The pinned reg is fixed to a certain value entirely user-controlled, so it generates nothing! e.enc64_rec(get_pinned_reg.bind(I64), rec_get_pinned_reg, 0); @@ -747,6 +749,8 @@ fn define_moves(e: &mut PerCpuModeEncodings, shared_defs: &SharedDefinitions, r: copy_to_ssa.bind(F32), rec_furm_reg_to_ssa.opcodes(&MOVSS_LOAD), ); + + e.enc_32_64_rec(dummy_sarg__, rec_dummy_sarg__, 0); } #[inline(never)] diff --git a/cranelift/codegen/meta/src/isa/x86/recipes.rs b/cranelift/codegen/meta/src/isa/x86/recipes.rs index 74645d0b59..2999ba73de 100644 --- a/cranelift/codegen/meta/src/isa/x86/recipes.rs +++ b/cranelift/codegen/meta/src/isa/x86/recipes.rs @@ -1270,6 +1270,12 @@ pub(crate) fn define<'shared>( ); } + recipes.add_recipe( + EncodingRecipeBuilder::new("dummy_sarg__", &formats.nullary, 0) + .operands_out(vec![Stack::new(gpr)]) + .emit(""), + ); + // XX+rd id with Abs4 function relocation. recipes.add_template_recipe( EncodingRecipeBuilder::new("fnaddr4", &formats.func_addr, 4) diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs index 1c06c4a325..f0b6f55733 100644 --- a/cranelift/codegen/meta/src/shared/instructions.rs +++ b/cranelift/codegen/meta/src/shared/instructions.rs @@ -1863,6 +1863,31 @@ pub(crate) fn define( .can_load(true), ); + let Sarg = &TypeVar::new( + "Sarg", + "Any scalar or vector type with as most 128 lanes", + TypeSetBuilder::new() + .specials(vec![crate::cdsl::types::SpecialType::StructArgument]) + .build(), + ); + let sarg__ = &Operand::new("sarg__", Sarg); + + // FIXME remove once the old style codegen backends are removed. + ig.push( + Inst::new( + "dummy_sarg__", + r#" + This creates a sarg__ + + This instruction is internal and should not be created by + Cranelift users. + "#, + &formats.nullary, + ) + .operands_in(vec![]) + .operands_out(vec![sarg__]), + ); + let src = &Operand::new("src", &imm.regunit); let dst = &Operand::new("dst", &imm.regunit); diff --git a/cranelift/codegen/src/abi.rs b/cranelift/codegen/src/abi.rs index cf4780e353..a281413e56 100644 --- a/cranelift/codegen/src/abi.rs +++ b/cranelift/codegen/src/abi.rs @@ -18,6 +18,10 @@ pub enum ArgAction { /// Assign the argument to the given location. Assign(ArgumentLoc), + /// Assign the argument to the given location and change the type to the specified type. + /// This is used by [`ArgumentPurpose::StructArgument`]. + AssignAndChangeType(ArgumentLoc, Type), + /// Convert the argument, then call again. /// /// This action can split an integer type into two smaller integer arguments, or it can split a @@ -119,6 +123,13 @@ pub fn legalize_args(args: &[AbiParam], aa: &mut AA) -> Option< args.to_mut()[argno].location = loc; argno += 1; } + // Assign argument to a location, change type to `INVALID` and move on to the next one. + ArgAction::AssignAndChangeType(loc, ty) => { + let arg = &mut args.to_mut()[argno]; + arg.location = loc; + arg.value_type = ty; + argno += 1; + } // Split this argument into two smaller ones. Then revisit both. ArgAction::Convert(conv) => { debug_assert!( diff --git a/cranelift/codegen/src/ir/extfunc.rs b/cranelift/codegen/src/ir/extfunc.rs index 6390f28a43..1db1c549e2 100644 --- a/cranelift/codegen/src/ir/extfunc.rs +++ b/cranelift/codegen/src/ir/extfunc.rs @@ -282,6 +282,9 @@ pub enum ArgumentPurpose { /// A normal user program value passed to or from a function. Normal, + /// A C struct passed as argument. + StructArgument(u32), + /// Struct return pointer. /// /// When a function needs to return more data than will fit in registers, the caller passes a @@ -334,21 +337,19 @@ pub enum ArgumentPurpose { StackLimit, } -/// Text format names of the `ArgumentPurpose` variants. -static PURPOSE_NAMES: [&str; 8] = [ - "normal", - "sret", - "link", - "fp", - "csr", - "vmctx", - "sigid", - "stack_limit", -]; - impl fmt::Display for ArgumentPurpose { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(PURPOSE_NAMES[*self as usize]) + f.write_str(match self { + Self::Normal => "normal", + Self::StructArgument(size) => return write!(f, "sarg({})", size), + Self::StructReturn => "sret", + Self::Link => "link", + Self::FramePointer => "fp", + Self::CalleeSaved => "csr", + Self::VMContext => "vmctx", + Self::SignatureId => "sigid", + Self::StackLimit => "stack_limit", + }) } } @@ -364,6 +365,13 @@ impl FromStr for ArgumentPurpose { "vmctx" => Ok(Self::VMContext), "sigid" => Ok(Self::SignatureId), "stack_limit" => Ok(Self::StackLimit), + _ if s.starts_with("sarg(") => { + if !s.ends_with(")") { + return Err(()); + } + let size: u32 = s["sarg(".len()..s.len() - 1].parse().map_err(|_| ())?; + Ok(Self::StructArgument(size)) + } _ => Err(()), } } @@ -436,16 +444,17 @@ mod tests { #[test] fn argument_purpose() { let all_purpose = [ - ArgumentPurpose::Normal, - ArgumentPurpose::StructReturn, - ArgumentPurpose::Link, - ArgumentPurpose::FramePointer, - ArgumentPurpose::CalleeSaved, - ArgumentPurpose::VMContext, - ArgumentPurpose::SignatureId, - ArgumentPurpose::StackLimit, + (ArgumentPurpose::Normal, "normal"), + (ArgumentPurpose::StructReturn, "sret"), + (ArgumentPurpose::Link, "link"), + (ArgumentPurpose::FramePointer, "fp"), + (ArgumentPurpose::CalleeSaved, "csr"), + (ArgumentPurpose::VMContext, "vmctx"), + (ArgumentPurpose::SignatureId, "sigid"), + (ArgumentPurpose::StackLimit, "stack_limit"), + (ArgumentPurpose::StructArgument(42), "sarg(42)"), ]; - for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) { + for &(e, n) in &all_purpose { assert_eq!(e.to_string(), n); assert_eq!(Ok(e), n.parse()); } diff --git a/cranelift/codegen/src/ir/stackslot.rs b/cranelift/codegen/src/ir/stackslot.rs index 5bb70d1f0e..cba28dede8 100644 --- a/cranelift/codegen/src/ir/stackslot.rs +++ b/cranelift/codegen/src/ir/stackslot.rs @@ -287,7 +287,12 @@ impl StackSlots { /// Create a stack slot representing an incoming function argument. pub fn make_incoming_arg(&mut self, ty: Type, offset: StackOffset) -> StackSlot { - let mut data = StackSlotData::new(StackSlotKind::IncomingArg, ty.bytes()); + self.make_incoming_struct_arg(ty.bytes(), offset) + } + + /// Create a stack slot representing an incoming struct function argument. + pub fn make_incoming_struct_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot { + let mut data = StackSlotData::new(StackSlotKind::IncomingArg, size); debug_assert!(offset <= StackOffset::max_value() - data.size as StackOffset); data.offset = Some(offset); self.push(data) @@ -301,8 +306,11 @@ impl StackSlots { /// The requested offset is relative to this function's stack pointer immediately before making /// the call. pub fn get_outgoing_arg(&mut self, ty: Type, offset: StackOffset) -> StackSlot { - let size = ty.bytes(); + self.get_outgoing_struct_arg(ty.bytes(), offset) + } + /// FIXME + pub fn get_outgoing_struct_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot { // Look for an existing outgoing stack slot with the same offset and size. let inspos = match self.outgoing.binary_search_by_key(&(offset, size), |&ss| { (self[ss].offset.unwrap(), self[ss].size) diff --git a/cranelift/codegen/src/ir/types.rs b/cranelift/codegen/src/ir/types.rs index c669839da5..dac123c468 100644 --- a/cranelift/codegen/src/ir/types.rs +++ b/cranelift/codegen/src/ir/types.rs @@ -343,6 +343,7 @@ impl Display for Type { f.write_str(match *self { IFLAGS => "iflags", FFLAGS => "fflags", + SARG__ => "sarg__", INVALID => panic!("INVALID encountered"), _ => panic!("Unknown Type(0x{:x})", self.0), }) diff --git a/cranelift/codegen/src/isa/aarch64/lower_inst.rs b/cranelift/codegen/src/isa/aarch64/lower_inst.rs index 88751a1478..3ba0f0ac10 100644 --- a/cranelift/codegen/src/isa/aarch64/lower_inst.rs +++ b/cranelift/codegen/src/isa/aarch64/lower_inst.rs @@ -2152,6 +2152,8 @@ pub(crate) fn lower_insn_to_regs>( panic!("x86-specific opcode in supposedly arch-neutral IR!"); } + Opcode::DummySarg => unreachable!(), + Opcode::AvgRound => unimplemented!(), Opcode::Iabs => unimplemented!(), Opcode::Snarrow diff --git a/cranelift/codegen/src/isa/x86/abi.rs b/cranelift/codegen/src/isa/x86/abi.rs index 6cd1175cd6..975934c100 100644 --- a/cranelift/codegen/src/isa/x86/abi.rs +++ b/cranelift/codegen/src/isa/x86/abi.rs @@ -108,6 +108,19 @@ impl Args { impl ArgAssigner for Args { fn assign(&mut self, arg: &AbiParam) -> ArgAction { + if let ArgumentPurpose::StructArgument(size) = arg.purpose { + if self.call_conv != CallConv::SystemV { + panic!( + "The sarg argument purpose is not yet implemented for non-systemv call conv {:?}", + self.call_conv, + ); + } + let loc = ArgumentLoc::Stack(self.offset as i32); + self.offset += size; + debug_assert!(self.offset <= i32::MAX as u32); + return ArgAction::AssignAndChangeType(loc, types::SARG__); + } + let ty = arg.value_type; if ty.bits() > u16::from(self.pointer_bits) { diff --git a/cranelift/codegen/src/legalizer/boundary.rs b/cranelift/codegen/src/legalizer/boundary.rs index aba52a8c2b..e9b7520e5f 100644 --- a/cranelift/codegen/src/legalizer/boundary.rs +++ b/cranelift/codegen/src/legalizer/boundary.rs @@ -22,8 +22,9 @@ use crate::cursor::{Cursor, FuncCursor}; use crate::flowgraph::ControlFlowGraph; use crate::ir::instructions::CallInfo; use crate::ir::{ - AbiParam, ArgumentLoc, ArgumentPurpose, Block, DataFlowGraph, Function, Inst, InstBuilder, - MemFlags, SigRef, Signature, StackSlotData, StackSlotKind, Type, Value, ValueLoc, + AbiParam, ArgumentLoc, ArgumentPurpose, Block, DataFlowGraph, ExtFuncData, ExternalName, + Function, Inst, InstBuilder, LibCall, MemFlags, SigRef, Signature, StackSlotData, + StackSlotKind, Type, Value, ValueLoc, }; use crate::isa::TargetIsa; use crate::legalizer::split::{isplit, vsplit}; @@ -113,12 +114,33 @@ fn legalize_entry_params(func: &mut Function, entry: Block) { let abi_type = pos.func.signature.params[abi_arg]; let arg_type = pos.func.dfg.value_type(arg); + match abi_type.purpose { + ArgumentPurpose::StructArgument(size) => { + let offset = if let ArgumentLoc::Stack(offset) = abi_type.location { + offset + } else { + unreachable!("StructArgument must already have a Stack ArgumentLoc assigned"); + }; + let ss = pos.func.stack_slots.make_incoming_struct_arg(size, offset); + let struct_arg = pos.ins().stack_addr(arg_type, ss, 0); + pos.func.dfg.change_to_alias(arg, struct_arg); + let dummy = pos + .func + .dfg + .append_block_param(entry, crate::ir::types::SARG__); + pos.func.locations[dummy] = ValueLoc::Stack(ss); + abi_arg += 1; + continue; + } + _ => {} + } if arg_type == abi_type.value_type { // No value translation is necessary, this argument matches the ABI type. // Just use the original block argument value. This is the most common case. pos.func.dfg.attach_block_param(entry, arg); match abi_type.purpose { ArgumentPurpose::Normal => {} + ArgumentPurpose::StructArgument(_) => unreachable!("Handled above"), ArgumentPurpose::FramePointer => {} ArgumentPurpose::CalleeSaved => {} ArgumentPurpose::StructReturn => { @@ -137,7 +159,7 @@ fn legalize_entry_params(func: &mut Function, entry: Block) { debug_assert!(!has_stack_limit, "Multiple stack_limit arguments found"); has_stack_limit = true; } - _ => panic!("Unexpected special-purpose arg {}", abi_type), + ArgumentPurpose::Link => panic!("Unexpected link arg {}", abi_type), } abi_arg += 1; } else { @@ -168,7 +190,7 @@ fn legalize_entry_params(func: &mut Function, entry: Block) { for &arg in &pos.func.signature.params[abi_arg..] { match arg.purpose { // Any normal parameters should have been processed above. - ArgumentPurpose::Normal => { + ArgumentPurpose::Normal | ArgumentPurpose::StructArgument(_) => { panic!("Leftover arg: {}", arg); } // The callee-save parameters should not appear until after register allocation is @@ -587,9 +609,20 @@ fn convert_to_abi( /// Check if a sequence of arguments match a desired sequence of argument types. fn check_arg_types(dfg: &DataFlowGraph, args: &[Value], types: &[AbiParam]) -> bool { - let arg_types = args.iter().map(|&v| dfg.value_type(v)); - let sig_types = types.iter().map(|&at| at.value_type); - arg_types.eq(sig_types) + let mut args = args.iter(); + let mut types = types.iter(); + loop { + match (args.next(), types.next()) { + (Some(&v), Some(at)) => { + if let ArgumentPurpose::StructArgument(_) = at.purpose { + } else if dfg.value_type(v) != at.value_type { + return false; + } + } + (Some(_), None) | (None, Some(_)) => return false, + (None, None) => return true, + } + } } /// Check if the arguments of the call `inst` match the signature. @@ -699,7 +732,12 @@ fn legalize_inst_arguments( .unwrap(); let mut put_arg = |func: &mut Function, arg| { let abi_type = get_abi_type(func, abi_arg); - if func.dfg.value_type(arg) == abi_type.value_type { + let struct_argument = if let ArgumentPurpose::StructArgument(_) = abi_type.purpose { + true + } else { + false + }; + if func.dfg.value_type(arg) == abi_type.value_type || struct_argument { // This is the argument type we need. vlist.as_mut_slice(&mut func.dfg.value_lists)[num_fixed_values + abi_arg] = arg; abi_arg += 1; @@ -762,7 +800,7 @@ pub fn handle_call_abi( // Start by checking if the argument types already match the signature. let sig_ref = match check_call_signature(&pos.func.dfg, inst) { - Ok(_) => return spill_call_arguments(pos), + Ok(_) => return spill_call_arguments(pos, isa), Err(s) => s, }; @@ -800,7 +838,7 @@ pub fn handle_call_abi( // Go back and insert spills for any stack arguments. pos.goto_inst(inst); - spill_call_arguments(pos); + spill_call_arguments(pos, isa); // Yes, we changed stuff. true @@ -990,7 +1028,8 @@ fn spill_entry_params(func: &mut Function, entry: Block) { .iter() .zip(func.dfg.block_params(entry)) { - if let ArgumentLoc::Stack(offset) = abi.location { + if let ArgumentPurpose::StructArgument(_) = abi.purpose { + } else if let ArgumentLoc::Stack(offset) = abi.location { let ss = func.stack_slots.make_incoming_arg(abi.value_type, offset); func.locations[arg] = ValueLoc::Stack(ss); } @@ -1005,7 +1044,7 @@ fn spill_entry_params(func: &mut Function, entry: Block) { /// TODO: The outgoing stack slots can be written a bit earlier, as long as there are no branches /// or calls between writing the stack slots and the call instruction. Writing the slots earlier /// could help reduce register pressure before the call. -fn spill_call_arguments(pos: &mut FuncCursor) -> bool { +fn spill_call_arguments(pos: &mut FuncCursor, isa: &dyn TargetIsa) -> bool { let inst = pos .current_inst() .expect("Cursor must point to a call instruction"); @@ -1032,9 +1071,15 @@ fn spill_call_arguments(pos: &mut FuncCursor) -> bool { // Assign `arg` to a new stack slot, unless it's already in the correct // slot. The legalization needs to be idempotent, so we should see a // correct outgoing slot on the second pass. - let ss = stack_slots.get_outgoing_arg(abi.value_type, offset); + let (ss, size) = match abi.purpose { + ArgumentPurpose::StructArgument(size) => ( + stack_slots.get_outgoing_struct_arg(size, offset), + Some(size), + ), + _ => (stack_slots.get_outgoing_arg(abi.value_type, offset), None), + }; if locations[arg] != ValueLoc::Stack(ss) { - Some((idx, arg, ss)) + Some((idx, arg, ss, size)) } else { None } @@ -1050,8 +1095,34 @@ fn spill_call_arguments(pos: &mut FuncCursor) -> bool { } // Insert the spill instructions and rewrite call arguments. - for (idx, arg, ss) in arglist { - let stack_val = pos.ins().spill(arg); + for (idx, arg, ss, size) in arglist { + let stack_val = if let Some(size) = size { + // Struct argument + let pointer_type = pos.func.dfg.value_type(arg); + let src = arg; + let dest = pos.ins().stack_addr(pointer_type, ss, 0); + let size = pos.ins().iconst(pointer_type, i64::from(size)); + let signature = { + let mut s = Signature::new(isa.default_call_conv()); + s.params.push(AbiParam::new(pointer_type)); + s.params.push(AbiParam::new(pointer_type)); + s.params.push(AbiParam::new(pointer_type)); + legalize_libcall_signature(&mut s, isa); + pos.func.import_signature(s) + }; + + let libc_memcpy = pos.func.import_function(ExtFuncData { + name: ExternalName::LibCall(LibCall::Memcpy), + signature, + colocated: false, + }); + + pos.ins().call(libc_memcpy, &[dest, src, size]); + pos.ins().dummy_sarg__() + } else { + // Non struct argument + pos.ins().spill(arg) + }; pos.func.locations[stack_val] = ValueLoc::Stack(ss); pos.func.dfg.inst_variable_args_mut(inst)[idx] = stack_val; } diff --git a/cranelift/codegen/src/verifier/mod.rs b/cranelift/codegen/src/verifier/mod.rs index 00fc31e212..aff4bcae26 100644 --- a/cranelift/codegen/src/verifier/mod.rs +++ b/cranelift/codegen/src/verifier/mod.rs @@ -65,9 +65,9 @@ use crate::ir; use crate::ir::entities::AnyEntity; use crate::ir::instructions::{BranchInfo, CallInfo, InstructionFormat, ResolvedConstraint}; use crate::ir::{ - types, ArgumentLoc, Block, Constant, FuncRef, Function, GlobalValue, Inst, InstructionData, - JumpTable, Opcode, SigRef, StackSlot, StackSlotKind, Type, Value, ValueDef, ValueList, - ValueLoc, + types, ArgumentLoc, ArgumentPurpose, Block, Constant, FuncRef, Function, GlobalValue, Inst, + InstructionData, JumpTable, Opcode, SigRef, StackSlot, StackSlotKind, Type, Value, ValueDef, + ValueList, ValueLoc, }; use crate::isa::TargetIsa; use crate::iterators::IteratorExtras; @@ -1473,7 +1473,8 @@ impl<'a> Verifier<'a> { ), )); } - if slot.size != abi.value_type.bytes() { + if abi.purpose == ArgumentPurpose::StructArgument(slot.size) { + } else if slot.size != abi.value_type.bytes() { return errors.fatal(( inst, self.context(inst), @@ -1986,6 +1987,20 @@ impl<'a> Verifier<'a> { )) }); + self.func + .signature + .returns + .iter() + .enumerate() + .for_each(|(i, ret)| { + if let ArgumentPurpose::StructArgument(_) = ret.purpose { + errors.report(( + AnyEntity::Function, + format!("Return value at position {} can't be an struct argument", i), + )) + } + }); + if errors.has_error() { Err(()) } else { diff --git a/cranelift/filetests/filetests/isa/x86/struct-arg.clif b/cranelift/filetests/filetests/isa/x86/struct-arg.clif new file mode 100644 index 0000000000..8a9d0177a4 --- /dev/null +++ b/cranelift/filetests/filetests/isa/x86/struct-arg.clif @@ -0,0 +1,57 @@ +test compile +set is_pic +target x86_64 + +function u0:0(i64 sarg(64)) -> i8 system_v { +block0(v0: i64): + v1 = load.i8 v0 + return v1 +} + +; check: function u0:0(sarg__ sarg(64) [0], i64 fp [%rbp]) -> i8 [%rax], i64 fp [%rbp] system_v { +; nextln: ss0 = incoming_arg 64, offset 0 +; nextln: ss1 = incoming_arg 16, offset -16 + +; check: block0(v3: sarg__ [ss0], v5: i64 [%rbp]): +; nextln: [RexOp1pushq#50] x86_push v5 +; nextln: [RexOp1copysp#8089] copy_special %rsp -> %rbp +; nextln: [RexOp1spaddr_id#808d,%rax] v2 = stack_addr.i64 ss0 +; nextln: v0 -> v2 +; nextln: [RexOp2ld#4b6,%rax] v4 = uload8.i32 v2 +; nextln: [null#00,%rax] v1 = ireduce.i8 v4 +; nextln: [RexOp1popq#58,%rbp] v6 = x86_pop.i64 +; nextln: [Op1ret#c3] return v1, v6 +; nextln: } + +function u0:1(i64) -> i8 system_v { +fn1 = u0:0(i64 sarg(64)) -> i8 system_v + +block0(v0: i64): + v1 = call fn1(v0) + return v1 +} + +; check: function u0:1(i64 [%rdi], i64 fp [%rbp]) -> i8 [%rax], i64 fp [%rbp] system_v { +; nextln: ss0 = outgoing_arg 64, offset 0 +; nextln: ss1 = incoming_arg 16, offset -16 +; nextln: sig0 = (sarg__ sarg(64) [0]) -> i8 [%rax] system_v +; nextln: sig1 = (i64 [%rdi], i64 [%rsi], i64 [%rdx]) system_v +; nextln: fn1 = u0:0 sig0 +; nextln: fn2 = %Memcpy sig1 + +; check: block0(v0: i64 [%rdi], v5: i64 [%rbp]): +; nextln: [RexOp1pushq#50] x86_push v5 +; nextln: [RexOp1copysp#8089] copy_special %rsp -> %rbp +; nextln: [RexOp1adjustsp_ib#d083] adjust_sp_down_imm 64 +; nextln: [RexOp1spaddr_id#808d,%rax] v2 = stack_addr.i64 ss0 +; nextln: [RexOp1pu_id#b8,%rcx] v3 = iconst.i64 64 +; nextln: [RexOp1rmov#8089] regmove v0, %rdi -> %rsi +; nextln: [RexOp1rmov#8089] regmove v2, %rax -> %rdi +; nextln: [RexOp1rmov#8089] regmove v3, %rcx -> %rdx +; nextln: [Op1call_plt_id#e8] call fn2(v2, v0, v3) +; nextln: [dummy_sarg__#00,ss0] v4 = dummy_sarg__ +; nextln: [Op1call_plt_id#e8,%rax] v1 = call fn1(v4) +; nextln: [RexOp1adjustsp_ib#8083] adjust_sp_up_imm 64 +; nextln: [RexOp1popq#58,%rbp] v6 = x86_pop.i64 +; nextln: [Op1ret#c3] return v1, v6 +; nextln: } diff --git a/cranelift/reader/src/lexer.rs b/cranelift/reader/src/lexer.rs index d1bb835eaa..72f94f8a06 100644 --- a/cranelift/reader/src/lexer.rs +++ b/cranelift/reader/src/lexer.rs @@ -329,6 +329,7 @@ impl<'a> Lexer<'a> { .unwrap_or_else(|| match text { "iflags" => Token::Type(types::IFLAGS), "fflags" => Token::Type(types::FFLAGS), + "sarg__" => Token::Type(types::SARG__), _ => Token::Identifier(text), }), loc, @@ -620,7 +621,7 @@ mod tests { let mut lex = Lexer::new( "v0 v00 vx01 block1234567890 block5234567890 v1x vx1 vxvx4 \ function0 function b1 i32x4 f32x5 \ - iflags fflags iflagss", + iflags fflags sarg__ iflagss", ); assert_eq!( lex.next(), @@ -643,6 +644,7 @@ mod tests { assert_eq!(lex.next(), token(Token::Identifier("f32x5"), 1)); assert_eq!(lex.next(), token(Token::Type(types::IFLAGS), 1)); assert_eq!(lex.next(), token(Token::Type(types::FFLAGS), 1)); + assert_eq!(lex.next(), token(Token::Type(types::SARG__), 1)); assert_eq!(lex.next(), token(Token::Identifier("iflagss"), 1)); assert_eq!(lex.next(), None); } diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 55e44f41f8..4d483847fe 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -15,10 +15,10 @@ use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, Va use cranelift_codegen::ir::types::INVALID; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{ - AbiParam, ArgumentExtension, ArgumentLoc, Block, Constant, ConstantData, ExtFuncData, - ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle, - JumpTable, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, StackSlotData, - StackSlotKind, Table, TableData, Type, Value, ValueLoc, + AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, Block, Constant, ConstantData, + ExtFuncData, ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, + HeapStyle, JumpTable, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, + StackSlotData, StackSlotKind, Table, TableData, Type, Value, ValueLoc, }; use cranelift_codegen::isa::{self, CallConv, Encoding, RegUnit, TargetIsa}; use cranelift_codegen::packed_option::ReservedValue; @@ -1423,6 +1423,14 @@ impl<'a> Parser<'a> { match s { "uext" => arg.extension = ArgumentExtension::Uext, "sext" => arg.extension = ArgumentExtension::Sext, + "sarg" => { + self.consume(); + self.match_token(Token::LPar, "expected '(' to begin sarg size")?; + let size = self.match_uimm32("expected byte-size in sarg decl")?; + self.match_token(Token::RPar, "expected ')' to end sarg size")?; + arg.purpose = ArgumentPurpose::StructArgument(size.into()); + continue; + } _ => { if let Ok(purpose) = s.parse() { arg.purpose = purpose;