Path: blob/main/cranelift/codegen/src/legalizer/mod.rs
1693 views
//! Legalize instructions.1//!2//! A legal instruction is one that can be mapped directly to a machine code instruction for the3//! target ISA. The `legalize_function()` function takes as input any function and transforms it4//! into an equivalent function using only legal instructions.5//!6//! The characteristics of legal instructions depend on the target ISA, so any given instruction7//! can be legal for one ISA and illegal for another.8//!9//! Besides transforming instructions, the legalizer also fills out the `function.encodings` map10//! which provides a legal encoding recipe for every instruction.11//!12//! The legalizer does not deal with register allocation constraints. These constraints are derived13//! from the encoding recipes, and solved later by the register allocator.1415use crate::cursor::{Cursor, FuncCursor};16use crate::ir::immediates::Imm64;17use crate::ir::types::{self, I64, I128};18use crate::ir::{self, InstBuilder, InstructionData, MemFlags, Value};19use crate::isa::TargetIsa;20use crate::trace;21use cranelift_entity::EntitySet;22use smallvec::SmallVec;2324mod branch_to_trap;25mod globalvalue;2627use self::branch_to_trap::BranchToTrap;28use self::globalvalue::expand_global_value;2930fn imm_const(pos: &mut FuncCursor, arg: Value, imm: Imm64, is_signed: bool) -> Value {31let ty = pos.func.dfg.value_type(arg);32match (ty, is_signed) {33(I128, true) => {34let imm = pos.ins().iconst(I64, imm);35pos.ins().sextend(I128, imm)36}37(I128, false) => {38let imm = pos.ins().iconst(I64, imm);39pos.ins().uextend(I128, imm)40}41_ => {42let bits = imm.bits();43let unsigned = match ty.lane_type() {44types::I8 => bits as u8 as i64,45types::I16 => bits as u16 as i64,46types::I32 => bits as u32 as i64,47types::I64 => bits,48_ => unreachable!(),49};50pos.ins().iconst(ty.lane_type(), unsigned)51}52}53}5455/// A command describing how the walk over instructions should proceed.56enum WalkCommand {57/// Continue walking to the next instruction, if any.58Continue,59/// Revisit the current instruction (presumably because it was legalized60/// into a new instruction that may also require further legalization).61Revisit,62}6364/// A simple, naive backwards walk over every instruction in every block in the65/// function's layout.66///67/// This does not guarantee any kind of reverse post-order visitation or68/// anything like that, it is just iterating over blocks in reverse layout69/// order, not any kind of control-flow graph visitation order.70///71/// The `f` visitor closure controls how the walk proceeds via its `WalkCommand`72/// result.73fn backward_walk(74func: &mut ir::Function,75mut f: impl FnMut(&mut ir::Function, ir::Block, ir::Inst) -> WalkCommand,76) {77let mut pos = FuncCursor::new(func);78while let Some(block) = pos.prev_block() {79let mut prev_pos;80while let Some(inst) = {81prev_pos = pos.position();82pos.prev_inst()83} {84match f(pos.func, block, inst) {85WalkCommand::Continue => continue,86WalkCommand::Revisit => pos.set_position(prev_pos),87}88}89}90}9192/// Perform a simple legalization by expansion of the function, without93/// platform-specific transforms.94pub fn simple_legalize(func: &mut ir::Function, isa: &dyn TargetIsa) {95trace!("Pre-legalization function:\n{}", func.display());9697let mut branch_to_trap = BranchToTrap::default();9899// We walk the IR backwards because in practice, given the way that100// frontends tend to produce CLIF, this means we will visit in roughly101// reverse post order, which is helpful for getting the most optimizations102// out of the `branch-to-trap` pass that we can (it must analyze trapping103// blocks before it can rewrite branches to them) but the order does not104// actually affect correctness.105backward_walk(func, |func, block, inst| match func.dfg.insts[inst] {106InstructionData::Trap {107opcode: ir::Opcode::Trap,108code: _,109} => {110branch_to_trap.analyze_trapping_block(func, block);111WalkCommand::Continue112}113InstructionData::Brif {114opcode: ir::Opcode::Brif,115arg,116blocks,117} => {118branch_to_trap.process_brif(func, inst, arg, blocks);119WalkCommand::Continue120}121122InstructionData::UnaryGlobalValue {123opcode: ir::Opcode::GlobalValue,124global_value,125} => expand_global_value(inst, func, isa, global_value),126127InstructionData::StackLoad {128opcode: ir::Opcode::StackLoad,129stack_slot,130offset,131} => expand_stack_load(isa, func, inst, stack_slot, offset),132133InstructionData::StackStore {134opcode: ir::Opcode::StackStore,135arg,136stack_slot,137offset,138} => expand_stack_store(isa, func, inst, arg, stack_slot, offset),139140InstructionData::DynamicStackLoad {141opcode: ir::Opcode::DynamicStackLoad,142dynamic_stack_slot,143} => expand_dynamic_stack_load(isa, func, inst, dynamic_stack_slot),144145InstructionData::DynamicStackStore {146opcode: ir::Opcode::DynamicStackStore,147arg,148dynamic_stack_slot,149} => expand_dynamic_stack_store(isa, func, inst, arg, dynamic_stack_slot),150151InstructionData::BinaryImm64 { opcode, arg, imm } => {152expand_binary_imm64(func, inst, opcode, arg, imm)153}154155InstructionData::IntCompareImm {156opcode: ir::Opcode::IcmpImm,157cond,158arg,159imm,160} => expand_icmp_imm(func, inst, cond, arg, imm),161162InstructionData::Binary { opcode, args } => expand_binary(func, inst, opcode, args),163164_ => WalkCommand::Continue,165});166167trace!("Post-legalization function:\n{}", func.display());168}169170fn expand_binary(171func: &mut ir::Function,172inst: ir::Inst,173opcode: ir::Opcode,174args: [ir::Value; 2],175) -> WalkCommand {176let mut pos = FuncCursor::new(func);177pos.goto_inst(inst);178179// Legalize the fused bitwise-plus-not instructions into simpler180// instructions to assist with optimizations. Lowering will pattern match181// this sequence regardless when architectures support the instruction182// natively.183match opcode {184ir::Opcode::BandNot => {185let neg = pos.ins().bnot(args[1]);186pos.func.dfg.replace(inst).band(args[0], neg);187}188ir::Opcode::BorNot => {189let neg = pos.ins().bnot(args[1]);190pos.func.dfg.replace(inst).bor(args[0], neg);191}192ir::Opcode::BxorNot => {193let neg = pos.ins().bnot(args[1]);194pos.func.dfg.replace(inst).bxor(args[0], neg);195}196_ => {}197}198199WalkCommand::Continue200}201202fn expand_icmp_imm(203func: &mut ir::Function,204inst: ir::Inst,205cond: ir::condcodes::IntCC,206arg: Value,207imm: Imm64,208) -> WalkCommand {209let mut pos = FuncCursor::new(func);210pos.goto_inst(inst);211212let imm = imm_const(&mut pos, arg, imm, true);213pos.func.dfg.replace(inst).icmp(cond, arg, imm);214215WalkCommand::Continue216}217218fn expand_binary_imm64(219func: &mut ir::Function,220inst: ir::Inst,221opcode: ir::Opcode,222arg: Value,223imm: Imm64,224) -> WalkCommand {225let mut pos = FuncCursor::new(func);226pos.goto_inst(inst);227228let is_signed = match opcode {229ir::Opcode::IaddImm230| ir::Opcode::IrsubImm231| ir::Opcode::ImulImm232| ir::Opcode::SdivImm233| ir::Opcode::SremImm => true,234_ => false,235};236237let imm = imm_const(&mut pos, arg, imm, is_signed);238239let replace = pos.func.dfg.replace(inst);240match opcode {241// bitops242ir::Opcode::BandImm => {243replace.band(arg, imm);244}245ir::Opcode::BorImm => {246replace.bor(arg, imm);247}248ir::Opcode::BxorImm => {249replace.bxor(arg, imm);250}251// bitshifting252ir::Opcode::IshlImm => {253replace.ishl(arg, imm);254}255ir::Opcode::RotlImm => {256replace.rotl(arg, imm);257}258ir::Opcode::RotrImm => {259replace.rotr(arg, imm);260}261ir::Opcode::SshrImm => {262replace.sshr(arg, imm);263}264ir::Opcode::UshrImm => {265replace.ushr(arg, imm);266}267// math268ir::Opcode::IaddImm => {269replace.iadd(arg, imm);270}271ir::Opcode::IrsubImm => {272// note: arg order reversed273replace.isub(imm, arg);274}275ir::Opcode::ImulImm => {276replace.imul(arg, imm);277}278ir::Opcode::SdivImm => {279replace.sdiv(arg, imm);280}281ir::Opcode::SremImm => {282replace.srem(arg, imm);283}284ir::Opcode::UdivImm => {285replace.udiv(arg, imm);286}287ir::Opcode::UremImm => {288replace.urem(arg, imm);289}290_ => {}291}292293WalkCommand::Continue294}295296fn expand_dynamic_stack_store(297isa: &dyn TargetIsa,298func: &mut ir::Function,299inst: ir::Inst,300arg: Value,301dynamic_stack_slot: ir::DynamicStackSlot,302) -> WalkCommand {303let mut pos = FuncCursor::new(func);304pos.goto_inst(inst);305pos.use_srcloc(inst);306307let vector_ty = pos.func.dfg.value_type(arg);308assert!(vector_ty.is_dynamic_vector());309310let addr_ty = isa.pointer_type();311let addr = pos.ins().dynamic_stack_addr(addr_ty, dynamic_stack_slot);312313let mut mflags = MemFlags::new();314// Stack slots are required to be accessible and aligned.315mflags.set_notrap();316mflags.set_aligned();317318pos.func.dfg.replace(inst).store(mflags, arg, addr, 0);319320WalkCommand::Continue321}322323fn expand_dynamic_stack_load(324isa: &dyn TargetIsa,325func: &mut ir::Function,326inst: ir::Inst,327dynamic_stack_slot: ir::DynamicStackSlot,328) -> WalkCommand {329let mut pos = FuncCursor::new(func).at_inst(inst);330pos.use_srcloc(inst);331332let ty = pos.func.dfg.value_type(pos.func.dfg.first_result(inst));333assert!(ty.is_dynamic_vector());334335let addr_ty = isa.pointer_type();336let addr = pos.ins().dynamic_stack_addr(addr_ty, dynamic_stack_slot);337338// Stack slots are required to be accessible and aligned.339let mflags = MemFlags::trusted();340341pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);342343WalkCommand::Continue344}345346fn expand_stack_store(347isa: &dyn TargetIsa,348func: &mut ir::Function,349inst: ir::Inst,350arg: ir::Value,351stack_slot: ir::StackSlot,352offset: ir::immediates::Offset32,353) -> WalkCommand {354let mut pos = FuncCursor::new(func).at_inst(inst);355pos.use_srcloc(inst);356357let addr_ty = isa.pointer_type();358let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);359360// Stack slots are required to be accessible.361// We can't currently ensure that they are aligned.362let mut mflags = MemFlags::new();363mflags.set_notrap();364365pos.func.dfg.replace(inst).store(mflags, arg, addr, 0);366367WalkCommand::Continue368}369370fn expand_stack_load(371isa: &dyn TargetIsa,372func: &mut ir::Function,373inst: ir::Inst,374stack_slot: ir::StackSlot,375offset: ir::immediates::Offset32,376) -> WalkCommand {377let mut pos = FuncCursor::new(func).at_inst(inst);378pos.use_srcloc(inst);379380let ty = pos.func.dfg.value_type(pos.func.dfg.first_result(inst));381let addr_ty = isa.pointer_type();382383let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);384385// Stack slots are required to be accessible.386// We can't currently ensure that they are aligned.387let mut mflags = MemFlags::new();388mflags.set_notrap();389390pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);391392WalkCommand::Continue393}394395396