//! Intel Instruction Set Architectures. pub mod settings; mod abi; mod binemit; mod enc_tables; mod registers; use binemit::{CodeSink, MemoryCodeSink, emit_function}; use super::super::settings as shared_settings; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings}; use isa::Builder as IsaBuilder; use isa::{TargetIsa, RegInfo, RegClass, EncInfo, RegUnit}; use self::registers::RU; use ir; use regalloc; use result; use ir::{InstBuilder, InstructionData, Opcode}; use stack_layout::layout_stack; use cursor::{Cursor, EncCursor}; #[allow(dead_code)] struct Isa { shared_flags: shared_settings::Flags, isa_flags: settings::Flags, cpumode: &'static [shared_enc_tables::Level1Entry], } /// Get an ISA builder for creating Intel targets. pub fn isa_builder() -> IsaBuilder { IsaBuilder { setup: settings::builder(), constructor: isa_constructor, } } fn isa_constructor( shared_flags: shared_settings::Flags, builder: &shared_settings::Builder, ) -> Box { let level1 = if shared_flags.is_64bit() { &enc_tables::LEVEL1_I64[..] } else { &enc_tables::LEVEL1_I32[..] }; Box::new(Isa { isa_flags: settings::Flags::new(&shared_flags, builder), shared_flags, cpumode: level1, }) } impl TargetIsa for Isa { fn name(&self) -> &'static str { "intel" } fn flags(&self) -> &shared_settings::Flags { &self.shared_flags } fn register_info(&self) -> RegInfo { registers::INFO.clone() } fn encoding_info(&self) -> EncInfo { enc_tables::INFO.clone() } fn legal_encodings<'a>( &'a self, dfg: &'a ir::DataFlowGraph, inst: &'a ir::InstructionData, ctrl_typevar: ir::Type, ) -> Encodings<'a> { lookup_enclist( ctrl_typevar, inst, dfg, self.cpumode, &enc_tables::LEVEL2[..], &enc_tables::ENCLISTS[..], &enc_tables::LEGALIZE_ACTIONS[..], &enc_tables::RECIPE_PREDICATES[..], &enc_tables::INST_PREDICATES[..], self.isa_flags.predicate_view(), ) } fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { abi::legalize_signature(sig, &self.shared_flags, current) } fn regclass_for_abi_type(&self, ty: ir::Type) -> RegClass { abi::regclass_for_abi_type(ty) } fn allocatable_registers(&self, func: &ir::Function) -> regalloc::AllocatableSet { abi::allocatable_registers(func, &self.shared_flags) } fn emit_inst( &self, func: &ir::Function, inst: ir::Inst, divert: &mut regalloc::RegDiversions, sink: &mut CodeSink, ) { binemit::emit_inst(func, inst, divert, sink) } fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) { emit_function(func, binemit::emit_inst, sink) } fn reloc_names(&self) -> &'static [&'static str] { &binemit::RELOC_NAMES } fn prologue_epilogue(&self, func: &mut ir::Function) -> result::CtonResult { let word_size = if self.flags().is_64bit() { 8 } else { 4 }; let mut csr_stack_size = word_size; // Size of RBP to start with for _reg in abi::CSR_GPRS.iter() { csr_stack_size += word_size; } let stack_offset = -(csr_stack_size as i32); let slot = ir::StackSlotData { kind: ir::StackSlotKind::IncomingArg, size: csr_stack_size, offset: stack_offset, }; func.create_stack_slot(slot); let total_stack_size = layout_stack(&mut func.stack_slots, word_size)?; let local_stack_size = total_stack_size - csr_stack_size; // Append frame pointer to function signature let rbp_arg = ir::AbiParam::special_reg( ir::types::I64, ir::ArgumentPurpose::FramePointer, RU::rbp as RegUnit, ); func.signature.params.push(rbp_arg); func.signature.returns.push(rbp_arg); for reg in abi::CSR_GPRS.iter() { let csr_arg = ir::AbiParam::special_reg( ir::types::I64, ir::ArgumentPurpose::CalleeSaved, *reg as RegUnit, ); func.signature.params.push(csr_arg); func.signature.returns.push(csr_arg); } // Append param to entry EBB let entry_ebb = func.layout.entry_block().expect("missing entry block"); func.dfg.append_ebb_param(entry_ebb, ir::types::I64); // Find our frame pointer parameter Value let fp = func.special_param(ir::ArgumentPurpose::FramePointer) .expect("missing frame pointer"); // Assign it a location func.locations[fp] = ir::ValueLoc::Reg(RU::rbp as RegUnit); let mut csr_vals = Vec::new(); for reg in abi::CSR_GPRS.iter() { // Append param to entry EBB func.dfg.append_ebb_param(entry_ebb, ir::types::I64); let csr_arg = func.dfg.ebb_params(entry_ebb).last().expect( "no last argument", ); // Assign it a location func.locations[*csr_arg] = ir::ValueLoc::Reg(*reg as RegUnit); // Remember it so we can push it momentarily csr_vals.push(*csr_arg); } // Insert prologue { let mut pos = EncCursor::new(func, self).at_first_insertion_point(entry_ebb); pos.ins().x86_push(fp); pos.ins().copy_special( RU::rsp as RegUnit, RU::rbp as RegUnit, ); if local_stack_size > 0 { pos.ins().adjust_sp_imm(-(local_stack_size as i32)); } for csr_arg in csr_vals { pos.ins().x86_push(csr_arg); } } // Find all 'return' instructions let mut return_insts = Vec::new(); for ebb in func.layout.ebbs() { for inst in func.layout.ebb_insts(ebb) { if let InstructionData::MultiAry { opcode, .. } = func.dfg[inst] { if opcode == Opcode::Return { return_insts.push(inst); } } } } // Insert an epilogue directly before every 'return' for inst in return_insts { let fp_ret = self.insert_epilogue(inst, local_stack_size as i32, func); func.locations[fp_ret] = ir::ValueLoc::Reg(RU::rbp as RegUnit); func.dfg.append_inst_arg(inst, fp_ret); } Ok(()) } } impl Isa { fn insert_epilogue( &self, inst: ir::Inst, stack_size: i32, func: &mut ir::Function, ) -> ir::Value { let mut pos = EncCursor::new(func, self).at_inst(inst); if stack_size > 0 { pos.ins().adjust_sp_imm(stack_size); } pos.ins().x86_pop(ir::types::I64) } }