From 528e6ff3f5a019223fc05c9a1cdfaa4da32f13e2 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Fri, 7 Jul 2017 11:33:38 -0700 Subject: [PATCH] Generate instruction unwrapping code for binemit recipes. Generate code to: - Unwrap the instruction and generate an error if the instruction format doesn't match the recipe. - Look up the value locations of register and stack arguments. The recipe_* functions in the ISA binemit modules now take these unwrapped items as arguments. Also add an optional `emit` argument to the EncRecipe constructor which makes it possible to provide inline Rust code snippets for code emission. This requires a lot less boilerplate than recipe_* functions. --- lib/cretonne/meta/cdsl/isa.py | 5 +- lib/cretonne/meta/gen_binemit.py | 105 +++++- lib/cretonne/meta/isa/riscv/recipes.py | 91 ++++- lib/cretonne/src/ir/valueloc.rs | 8 + lib/cretonne/src/isa/intel/binemit.rs | 465 +++++++++++++------------ lib/cretonne/src/isa/riscv/binemit.rs | 180 ---------- 6 files changed, 435 insertions(+), 419 deletions(-) diff --git a/lib/cretonne/meta/cdsl/isa.py b/lib/cretonne/meta/cdsl/isa.py index bc85f7fb8f..0562df10dc 100644 --- a/lib/cretonne/meta/cdsl/isa.py +++ b/lib/cretonne/meta/cdsl/isa.py @@ -202,6 +202,7 @@ class EncRecipe(object): :param: branch_range `(origin, bits)` range for branches. :param: instp Instruction predicate. :param: isap ISA predicate. + :param: emit Rust code for binary emission. """ def __init__( @@ -213,7 +214,8 @@ class EncRecipe(object): outs, # type: ConstraintSeq branch_range=None, # type: BranchRange instp=None, # type: PredNode - isap=None # type: PredNode + isap=None, # type: PredNode + emit=None # type: str ): # type: (...) -> None self.name = name @@ -223,6 +225,7 @@ class EncRecipe(object): self.branch_range = branch_range self.instp = instp self.isap = isap + self.emit = emit if instp: assert instp.predicate_context() == format self.number = None # type: int diff --git a/lib/cretonne/meta/gen_binemit.py b/lib/cretonne/meta/gen_binemit.py index 8d1954ce55..858fc36b39 100644 --- a/lib/cretonne/meta/gen_binemit.py +++ b/lib/cretonne/meta/gen_binemit.py @@ -3,15 +3,108 @@ Generate binary emission code for each ISA. """ from __future__ import absolute_import +from cdsl.registers import RegClass, Stack import srcgen try: from typing import Sequence, List # noqa - from cdsl.isa import TargetISA # noqa + from cdsl.isa import TargetISA, EncRecipe # noqa except ImportError: pass +def gen_recipe(recipe, fmt): + # type: (EncRecipe, srcgen.Formatter) -> None + """ + Generate code to handle a single recipe. + + - Unpack the instruction data, knowing the format. + - Determine register locations for operands with register constraints. + - Determine stack slot locations for operands with stack constraints. + - Call hand-written code for the actual emission. + """ + iform = recipe.format + nvops = iform.num_value_operands + want_args = any(isinstance(i, RegClass) or isinstance(i, Stack) + for i in recipe.ins) + assert not want_args or nvops > 0 + want_outs = any(isinstance(o, RegClass) or isinstance(o, Stack) + for o in recipe.outs) + + # First unpack the instruction. + with fmt.indented( + 'if let InstructionData::{} {{'.format(iform.name), + '}'): + for f in iform.imm_fields: + fmt.line('{},'.format(f.member)) + if want_args: + if iform.has_value_list or nvops > 1: + fmt.line('ref args,') + else: + fmt.line('arg,') + fmt.line('..') + fmt.outdented_line('} = func.dfg[inst] {') + + # Normalize to an `args` array. + if want_args: + if iform.has_value_list: + fmt.line('let args = args.as_slice(&func.dfg.value_lists);') + elif nvops == 1: + fmt.line('let args = [arg];') + + # Unwrap interesting input arguments. + # Don't bother with fixed registers. + args = '' + for i, arg in enumerate(recipe.ins): + if isinstance(arg, RegClass): + v = 'in_reg{}'.format(i) + args += ', ' + v + fmt.line( + 'let {} = func.locations[args[{}]].unwrap_reg();' + .format(v, i)) + elif isinstance(arg, Stack): + v = 'in_ss{}'.format(i) + args += ', ' + v + fmt.line( + 'let {} = func.locations[args[{}]].unwrap_stack();' + .format(v, i)) + + # Pass arguments in this order: inputs, imm_fields, outputs. + for f in iform.imm_fields: + args += ', ' + f.member + + # Unwrap interesting output arguments. + if want_outs: + if len(recipe.outs) == 1: + fmt.line('let results = [func.dfg.first_result(inst)];') + else: + fmt.line('let results = func.dfg.inst_results(inst);') + for i, res in enumerate(recipe.outs): + if isinstance(res, RegClass): + v = 'out_reg{}'.format(i) + args += ', ' + v + fmt.line( + 'let {} = func.locations[results[{}]].unwrap_reg();' + .format(v, i)) + elif isinstance(res, Stack): + v = 'out_ss{}'.format(i) + args += ', ' + v + fmt.line( + 'let {} = func.locations[results[{}]].unwrap_stack();' + .format(v, i)) + + # Call hand-written code. If the recipe contains a code snippet, use + # that. Otherwise cal a recipe function in the target ISA's binemit + # module. + if recipe.emit is None: + fmt.format( + 'return recipe_{}(func, inst, sink, bits{});', + recipe.name.lower(), args) + else: + fmt.multi_line(recipe.emit) + fmt.line('return;') + + def gen_isa(isa, fmt): # type: (TargetISA, srcgen.Formatter) -> None """ @@ -28,14 +121,18 @@ def gen_isa(isa, fmt): '(func: &Function, inst: Inst, _sink: &mut CS) {', '}'): fmt.line('bad_encoding(func, inst)') else: + fmt.line('#[allow(unused_variables, unreachable_code)]') with fmt.indented( 'pub fn emit_inst' '(func: &Function, inst: Inst, sink: &mut CS) {', '}'): + fmt.line('let bits = func.encodings[inst].bits();') with fmt.indented('match func.encodings[inst].recipe() {', '}'): for i, recipe in enumerate(isa.all_recipes): - fmt.line('{} => recipe_{}(func, inst, sink),'.format( - i, recipe.name.lower())) - fmt.line('_ => bad_encoding(func, inst),') + fmt.comment(recipe.name) + with fmt.indented('{} => {{'.format(i), '}'): + gen_recipe(recipe, fmt) + fmt.line('_ => {}') + fmt.line('bad_encoding(func, inst);') def generate(isas, out_dir): diff --git a/lib/cretonne/meta/isa/riscv/recipes.py b/lib/cretonne/meta/isa/riscv/recipes.py index 2ea7f35597..510e13c860 100644 --- a/lib/cretonne/meta/isa/riscv/recipes.py +++ b/lib/cretonne/meta/isa/riscv/recipes.py @@ -92,68 +92,123 @@ def LUI(): # R-type 32-bit instructions: These are mostly binary arithmetic instructions. # The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8) -R = EncRecipe('R', Binary, size=4, ins=(GPR, GPR), outs=GPR) +R = EncRecipe( + 'R', Binary, size=4, ins=(GPR, GPR), outs=GPR, + emit='put_r(bits, in_reg0, in_reg1, out_reg0, sink);') # R-type with an immediate shift amount instead of rs2. -Rshamt = EncRecipe('Rshamt', BinaryImm, size=4, ins=GPR, outs=GPR) +Rshamt = EncRecipe( + 'Rshamt', BinaryImm, size=4, ins=GPR, outs=GPR, + emit='put_rshamt(bits, in_reg0, imm.into(), out_reg0, sink);') # R-type encoding of an integer comparison. -Ricmp = EncRecipe('Ricmp', IntCompare, size=4, ins=(GPR, GPR), outs=GPR) +Ricmp = EncRecipe( + 'Ricmp', IntCompare, size=4, ins=(GPR, GPR), outs=GPR, + emit='put_r(bits, in_reg0, in_reg1, out_reg0, sink);') I = EncRecipe( 'I', BinaryImm, size=4, ins=GPR, outs=GPR, - instp=IsSignedInt(BinaryImm.imm, 12)) + instp=IsSignedInt(BinaryImm.imm, 12), + emit='put_i(bits, in_reg0, imm.into(), out_reg0, sink);') # I-type instruction with a hardcoded %x0 rs1. Iz = EncRecipe( 'Iz', UnaryImm, size=4, ins=(), outs=GPR, - instp=IsSignedInt(UnaryImm.imm, 12)) + instp=IsSignedInt(UnaryImm.imm, 12), + emit='put_i(bits, 0, imm.into(), out_reg0, sink);') # I-type encoding of an integer comparison. Iicmp = EncRecipe( 'Iicmp', IntCompareImm, size=4, ins=GPR, outs=GPR, - instp=IsSignedInt(IntCompareImm.imm, 12)) + instp=IsSignedInt(IntCompareImm.imm, 12), + emit='put_i(bits, in_reg0, imm.into(), out_reg0, sink);') # I-type encoding for `jalr` as a return instruction. We won't use the # immediate offset. # The variable return values are not encoded. -Iret = EncRecipe('Iret', MultiAry, size=4, ins=(), outs=()) +Iret = EncRecipe( + 'Iret', MultiAry, size=4, ins=(), outs=(), + emit=''' + // Return instructions are always a jalr to %x1. + // The return address is provided as a special-purpose link argument. + put_i(bits, + 1, // rs1 = %x1 + 0, // no offset. + 0, // rd = %x0: no address written. + sink); + ''') # I-type encoding for `jalr` as an indirect call. -Icall = EncRecipe('Icall', IndirectCall, size=4, ins=GPR, outs=()) +Icall = EncRecipe( + 'Icall', IndirectCall, size=4, ins=GPR, outs=(), + emit=''' + // Indirect instructions are jalr with rd=%x1. + put_i(bits, + in_reg0, + 0, // no offset. + 1, // rd = %x1: link register. + sink); + ''') + # Copy of a GPR is implemented as addi x, 0. -Icopy = EncRecipe('Icopy', Unary, size=4, ins=GPR, outs=GPR) +Icopy = EncRecipe( + 'Icopy', Unary, size=4, ins=GPR, outs=GPR, + emit='put_i(bits, in_reg0, 0, out_reg0, sink);') # U-type instructions have a 20-bit immediate that targets bits 12-31. U = EncRecipe( 'U', UnaryImm, size=4, ins=(), outs=GPR, - instp=IsSignedInt(UnaryImm.imm, 32, 12)) + instp=IsSignedInt(UnaryImm.imm, 32, 12), + emit='put_u(bits, imm.into(), out_reg0, sink);') # UJ-type unconditional branch instructions. -UJ = EncRecipe('UJ', Jump, size=4, ins=(), outs=(), branch_range=(0, 21)) -UJcall = EncRecipe('UJcall', Call, size=4, ins=(), outs=()) +UJ = EncRecipe( + 'UJ', Jump, size=4, ins=(), outs=(), branch_range=(0, 21), + emit=''' + let dest = func.offsets[destination] as i64; + let disp = dest - sink.offset() as i64; + put_uj(bits, disp, 0, sink); + ''') + +UJcall = EncRecipe( + 'UJcall', Call, size=4, ins=(), outs=(), + emit=''' + sink.reloc_func(RelocKind::Call.into(), func_ref); + // rd=%x1 is the standard link register. + put_uj(bits, 0, 1, sink); + ''') # SB-type branch instructions. -# TODO: These instructions have a +/- 4 KB branch range. How to encode that -# constraint? SB = EncRecipe( 'SB', BranchIcmp, size=4, ins=(GPR, GPR), outs=(), - branch_range=(0, 13)) + branch_range=(0, 13), + emit=''' + let dest = func.offsets[destination] as i64; + let disp = dest - sink.offset() as i64; + put_sb(bits, disp, in_reg0, in_reg1, sink); + ''') # SB-type branch instruction with rs2 fixed to zero. SBzero = EncRecipe( 'SBzero', Branch, size=4, ins=(GPR), outs=(), - branch_range=(0, 13)) + branch_range=(0, 13), + emit=''' + let dest = func.offsets[destination] as i64; + let disp = dest - sink.offset() as i64; + put_sb(bits, disp, in_reg0, 0, sink); + ''') # Spill of a GPR. GPsp = EncRecipe( 'GPsp', Unary, size=4, - ins=GPR, outs=Stack(GPR)) + ins=GPR, outs=Stack(GPR), + emit='unimplemented!();') # Fill of a GPR. GPfi = EncRecipe( 'GPfi', Unary, size=4, - ins=Stack(GPR), outs=GPR) + ins=Stack(GPR), outs=GPR, + emit='unimplemented!();') diff --git a/lib/cretonne/src/ir/valueloc.rs b/lib/cretonne/src/ir/valueloc.rs index cae3042170..a6a358e14b 100644 --- a/lib/cretonne/src/ir/valueloc.rs +++ b/lib/cretonne/src/ir/valueloc.rs @@ -33,6 +33,14 @@ impl ValueLoc { } } + /// Get the stack slot of this location, or panic. + pub fn unwrap_stack(self) -> StackSlot { + match self { + ValueLoc::Stack(ss) => ss, + _ => panic!("Expected stack slot: {:?}", self), + } + } + /// Return an object that can display this value location, using the register info from the /// target ISA. pub fn display<'a, R: Into>>(self, regs: R) -> DisplayValueLoc<'a> { diff --git a/lib/cretonne/src/isa/intel/binemit.rs b/lib/cretonne/src/isa/intel/binemit.rs index 10bd6288a7..831ef4c756 100644 --- a/lib/cretonne/src/isa/intel/binemit.rs +++ b/lib/cretonne/src/isa/intel/binemit.rs @@ -1,7 +1,8 @@ //! Emitting binary Intel machine code. use binemit::{CodeSink, Reloc, bad_encoding}; -use ir::{Function, Inst, InstructionData}; +use ir::{self, Function, Inst, InstructionData, MemFlags}; +use ir::immediates::{Imm64, Offset32}; use isa::RegUnit; include!(concat!(env!("OUT_DIR"), "/binemit-intel.rs")); @@ -100,257 +101,289 @@ fn modrm_disp32(rm: RegUnit, reg: RegUnit, sink: &mut CS) sink.put1(b); } -fn recipe_op1rr(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Binary { args, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - modrm_rr(func.locations[args[0]].unwrap_reg(), - func.locations[args[1]].unwrap_reg(), - sink); - } else { - panic!("Expected Binary format: {:?}", func.dfg[inst]); - } +fn recipe_op1rr(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit) { + put_op1(bits, sink); + modrm_rr(in_reg0, in_reg1, sink); } -fn recipe_op1ur(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Unary { arg, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - let res = func.locations[func.dfg.first_result(inst)].unwrap_reg(); - modrm_rr(res, func.locations[arg].unwrap_reg(), sink); - } else { - panic!("Expected Unary format: {:?}", func.dfg[inst]); - } +fn recipe_op1ur(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + out_reg0: RegUnit) { + put_op1(bits, sink); + modrm_rr(out_reg0, in_reg0, sink); } -fn recipe_op1rc(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Binary { args, .. } = func.dfg[inst] { - let bits = func.encodings[inst].bits(); - put_op1(bits, sink); - modrm_r_bits(func.locations[args[0]].unwrap_reg(), bits, sink); - } else { - panic!("Expected Binary format: {:?}", func.dfg[inst]); - } +fn recipe_op1rc(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit) { + put_op1(bits, sink); + modrm_r_bits(in_reg0, bits, sink); } -fn recipe_op1rib(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::BinaryImm { arg, imm, .. } = func.dfg[inst] { - let bits = func.encodings[inst].bits(); - put_op1(bits, sink); - modrm_r_bits(func.locations[arg].unwrap_reg(), bits, sink); - let imm: i64 = imm.into(); - sink.put1(imm as u8); - } else { - panic!("Expected BinaryImm format: {:?}", func.dfg[inst]); - } +fn recipe_op1rib(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + imm: Imm64) { + put_op1(bits, sink); + modrm_r_bits(in_reg0, bits, sink); + let imm: i64 = imm.into(); + sink.put1(imm as u8); } -fn recipe_op1rid(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::BinaryImm { arg, imm, .. } = func.dfg[inst] { - let bits = func.encodings[inst].bits(); - put_op1(bits, sink); - modrm_r_bits(func.locations[arg].unwrap_reg(), bits, sink); - let imm: i64 = imm.into(); - sink.put4(imm as u32); - } else { - panic!("Expected BinaryImm format: {:?}", func.dfg[inst]); - } +fn recipe_op1rid(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + imm: Imm64) { + put_op1(bits, sink); + modrm_r_bits(in_reg0, bits, sink); + let imm: i64 = imm.into(); + sink.put4(imm as u32); } -fn recipe_op1uid(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::UnaryImm { imm, .. } = func.dfg[inst] { - let bits = func.encodings[inst].bits(); - let reg = func.locations[func.dfg.first_result(inst)].unwrap_reg(); - // The destination register is encoded in the low bits of the opcode. No ModR/M - put_op1(bits | (reg & 7), sink); - let imm: i64 = imm.into(); - sink.put4(imm as u32); - } else { - panic!("Expected UnaryImm format: {:?}", func.dfg[inst]); - } +fn recipe_op1uid(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + imm: Imm64, + out_reg0: RegUnit) { + // The destination register is encoded in the low bits of the opcode. No ModR/M + put_op1(bits | (out_reg0 & 7), sink); + let imm: i64 = imm.into(); + sink.put4(imm as u32); } // Store recipes. -fn recipe_op1st(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Store { args, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - modrm_rm(func.locations[args[1]].unwrap_reg(), - func.locations[args[0]].unwrap_reg(), - sink); - } else { - panic!("Expected Store format: {:?}", func.dfg[inst]); - } +fn recipe_op1st(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + _flags: MemFlags, + _offset: Offset32) { + put_op1(bits, sink); + modrm_rm(in_reg1, in_reg0, sink); } // This is just a tighter register class constraint. -fn recipe_op1st_abcd(func: &Function, inst: Inst, sink: &mut CS) { - recipe_op1st(func, inst, sink) +fn recipe_op1st_abcd(func: &Function, + inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + flags: MemFlags, + offset: Offset32) { + recipe_op1st(func, inst, sink, bits, in_reg0, in_reg1, flags, offset) } -fn recipe_mp1st(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Store { args, .. } = func.dfg[inst] { - put_mp1(func.encodings[inst].bits(), sink); - modrm_rm(func.locations[args[1]].unwrap_reg(), - func.locations[args[0]].unwrap_reg(), - sink); - } else { - panic!("Expected Store format: {:?}", func.dfg[inst]); - } +fn recipe_mp1st(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + _flags: MemFlags, + _offset: Offset32) { + put_mp1(bits, sink); + modrm_rm(in_reg1, in_reg0, sink); } -fn recipe_op1stdisp8(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Store { args, offset, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - modrm_disp8(func.locations[args[1]].unwrap_reg(), - func.locations[args[0]].unwrap_reg(), - sink); - let offset: i32 = offset.into(); - sink.put1(offset as u8); - } else { - panic!("Expected Store format: {:?}", func.dfg[inst]); - } +fn recipe_op1stdisp8(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + _flags: MemFlags, + offset: Offset32) { + put_op1(bits, sink); + modrm_disp8(in_reg1, in_reg0, sink); + let offset: i32 = offset.into(); + sink.put1(offset as u8); } -fn recipe_op1stdisp8_abcd(func: &Function, inst: Inst, sink: &mut CS) { - recipe_op1stdisp8(func, inst, sink) +fn recipe_op1stdisp8_abcd(func: &Function, + inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + flags: MemFlags, + offset: Offset32) { + recipe_op1stdisp8(func, inst, sink, bits, in_reg0, in_reg1, flags, offset) } -fn recipe_mp1stdisp8(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Store { args, offset, .. } = func.dfg[inst] { - put_mp1(func.encodings[inst].bits(), sink); - modrm_disp8(func.locations[args[1]].unwrap_reg(), - func.locations[args[0]].unwrap_reg(), - sink); - let offset: i32 = offset.into(); - sink.put1(offset as u8); - } else { - panic!("Expected Store format: {:?}", func.dfg[inst]); - } +fn recipe_mp1stdisp8(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + _flags: MemFlags, + offset: Offset32) { + put_mp1(bits, sink); + modrm_disp8(in_reg1, in_reg0, sink); + let offset: i32 = offset.into(); + sink.put1(offset as u8); } -fn recipe_op1stdisp32(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Store { args, offset, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - modrm_disp32(func.locations[args[1]].unwrap_reg(), - func.locations[args[0]].unwrap_reg(), - sink); - let offset: i32 = offset.into(); - sink.put4(offset as u32); - } else { - panic!("Expected Store format: {:?}", func.dfg[inst]); - } +fn recipe_op1stdisp32(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + _flags: MemFlags, + offset: Offset32) { + put_op1(bits, sink); + modrm_disp32(in_reg1, in_reg0, sink); + let offset: i32 = offset.into(); + sink.put4(offset as u32); } -fn recipe_op1stdisp32_abcd(func: &Function, inst: Inst, sink: &mut CS) { - recipe_op1stdisp32(func, inst, sink) +fn recipe_op1stdisp32_abcd(func: &Function, + inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + flags: MemFlags, + offset: Offset32) { + recipe_op1stdisp32(func, inst, sink, bits, in_reg0, in_reg1, flags, offset) } -fn recipe_mp1stdisp32(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Store { args, offset, .. } = func.dfg[inst] { - put_mp1(func.encodings[inst].bits(), sink); - modrm_disp32(func.locations[args[1]].unwrap_reg(), - func.locations[args[0]].unwrap_reg(), - sink); - let offset: i32 = offset.into(); - sink.put4(offset as u32); - } else { - panic!("Expected Store format: {:?}", func.dfg[inst]); - } +fn recipe_mp1stdisp32(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + in_reg1: RegUnit, + _flags: MemFlags, + offset: Offset32) { + put_mp1(bits, sink); + modrm_disp32(in_reg1, in_reg0, sink); + let offset: i32 = offset.into(); + sink.put4(offset as u32); } // Load recipes -fn recipe_op1ld(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Load { arg, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - modrm_rm(func.locations[arg].unwrap_reg(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected Load format: {:?}", func.dfg[inst]); - } -} - -fn recipe_op1lddisp8(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Load { arg, offset, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - modrm_disp8(func.locations[arg].unwrap_reg(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - let offset: i32 = offset.into(); - sink.put1(offset as u8); - } else { - panic!("Expected Load format: {:?}", func.dfg[inst]); - } -} - -fn recipe_op1lddisp32(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Load { arg, offset, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - modrm_disp32(func.locations[arg].unwrap_reg(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - let offset: i32 = offset.into(); - sink.put4(offset as u32); - } else { - panic!("Expected Load format: {:?}", func.dfg[inst]); - } -} - -fn recipe_op2ld(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Load { arg, .. } = func.dfg[inst] { - put_op2(func.encodings[inst].bits(), sink); - modrm_rm(func.locations[arg].unwrap_reg(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected Load format: {:?}", func.dfg[inst]); - } -} - -fn recipe_op2lddisp8(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Load { arg, offset, .. } = func.dfg[inst] { - put_op2(func.encodings[inst].bits(), sink); - modrm_disp8(func.locations[arg].unwrap_reg(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - let offset: i32 = offset.into(); - sink.put1(offset as u8); - } else { - panic!("Expected Load format: {:?}", func.dfg[inst]); - } -} - -fn recipe_op2lddisp32(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Load { arg, offset, .. } = func.dfg[inst] { - put_op2(func.encodings[inst].bits(), sink); - modrm_disp32(func.locations[arg].unwrap_reg(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - let offset: i32 = offset.into(); - sink.put4(offset as u32); - } else { - panic!("Expected Load format: {:?}", func.dfg[inst]); - } -} - -fn recipe_op1call_id(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Call { func_ref, .. } = func.dfg[inst] { - put_op1(func.encodings[inst].bits(), sink); - sink.reloc_func(RelocKind::PCRel4.into(), func_ref); - sink.put4(0); - } else { - panic!("Expected Call format: {:?}", func.dfg[inst]); - } -} - -fn recipe_op1call_r(func: &Function, inst: Inst, sink: &mut CS) { - let bits = func.encodings[inst].bits(); +fn recipe_op1ld(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + _flags: MemFlags, + _offset: Offset32, + out_reg0: RegUnit) { put_op1(bits, sink); - modrm_r_bits(func.locations[func.dfg.inst_args(inst)[0]].unwrap_reg(), - bits, - sink); + modrm_rm(in_reg0, out_reg0, sink); } -fn recipe_op1ret(func: &Function, inst: Inst, sink: &mut CS) { - put_op1(func.encodings[inst].bits(), sink); +fn recipe_op1lddisp8(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + _flags: MemFlags, + offset: Offset32, + out_reg0: RegUnit) { + put_op1(bits, sink); + modrm_disp8(in_reg0, out_reg0, sink); + let offset: i32 = offset.into(); + sink.put1(offset as u8); +} + +fn recipe_op1lddisp32(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + _flags: MemFlags, + offset: Offset32, + out_reg0: RegUnit) { + put_op1(bits, sink); + modrm_disp32(in_reg0, out_reg0, sink); + let offset: i32 = offset.into(); + sink.put4(offset as u32); +} + +fn recipe_op2ld(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + _flags: MemFlags, + _offset: Offset32, + out_reg0: RegUnit) { + put_op2(bits, sink); + modrm_rm(in_reg0, out_reg0, sink); +} + +fn recipe_op2lddisp8(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + _flags: MemFlags, + offset: Offset32, + out_reg0: RegUnit) { + put_op2(bits, sink); + modrm_disp8(in_reg0, out_reg0, sink); + let offset: i32 = offset.into(); + sink.put1(offset as u8); +} + +fn recipe_op2lddisp32(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + _flags: MemFlags, + offset: Offset32, + out_reg0: RegUnit) { + put_op2(bits, sink); + modrm_disp32(in_reg0, out_reg0, sink); + let offset: i32 = offset.into(); + sink.put4(offset as u32); +} + +fn recipe_op1call_id(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + func_ref: ir::FuncRef) { + put_op1(bits, sink); + sink.reloc_func(RelocKind::PCRel4.into(), func_ref); + sink.put4(0); +} + +fn recipe_op1call_r(_func: &Function, + _inst: Inst, + sink: &mut CS, + bits: u16, + in_reg0: RegUnit, + _sig_ref: ir::SigRef) { + put_op1(bits, sink); + modrm_r_bits(in_reg0, bits, sink); +} + +fn recipe_op1ret(_func: &Function, _inst: Inst, sink: &mut CS, bits: u16) { + put_op1(bits, sink); } diff --git a/lib/cretonne/src/isa/riscv/binemit.rs b/lib/cretonne/src/isa/riscv/binemit.rs index 21638980b2..d193981887 100644 --- a/lib/cretonne/src/isa/riscv/binemit.rs +++ b/lib/cretonne/src/isa/riscv/binemit.rs @@ -87,42 +87,6 @@ fn put_rshamt(bits: u16, sink.put4(i); } -fn recipe_r(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Binary { args, .. } = func.dfg[inst] { - put_r(func.encodings[inst].bits(), - func.locations[args[0]].unwrap_reg(), - func.locations[args[1]].unwrap_reg(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected Binary format: {:?}", func.dfg[inst]); - } -} - -fn recipe_ricmp(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::IntCompare { args, .. } = func.dfg[inst] { - put_r(func.encodings[inst].bits(), - func.locations[args[0]].unwrap_reg(), - func.locations[args[1]].unwrap_reg(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected IntCompare format: {:?}", func.dfg[inst]); - } -} - -fn recipe_rshamt(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::BinaryImm { arg, imm, .. } = func.dfg[inst] { - put_rshamt(func.encodings[inst].bits(), - func.locations[arg].unwrap_reg(), - imm.into(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected BinaryImm format: {:?}", func.dfg[inst]); - } -} - /// I-type instructions. /// /// 31 19 14 11 6 @@ -148,73 +112,6 @@ fn put_i(bits: u16, rs1: RegUnit, imm: i64, rd: RegUnit, sink.put4(i); } -fn recipe_i(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::BinaryImm { arg, imm, .. } = func.dfg[inst] { - put_i(func.encodings[inst].bits(), - func.locations[arg].unwrap_reg(), - imm.into(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected BinaryImm format: {:?}", func.dfg[inst]); - } -} - -fn recipe_iz(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::UnaryImm { imm, .. } = func.dfg[inst] { - put_i(func.encodings[inst].bits(), - 0, - imm.into(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected UnaryImm format: {:?}", func.dfg[inst]); - } -} - -fn recipe_iicmp(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::IntCompareImm { arg, imm, .. } = func.dfg[inst] { - put_i(func.encodings[inst].bits(), - func.locations[arg].unwrap_reg(), - imm.into(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected IntCompareImm format: {:?}", func.dfg[inst]); - } -} - -fn recipe_iret(func: &Function, inst: Inst, sink: &mut CS) { - // Return instructions are always a jalr to %x1. - // The return address is provided as a special-purpose link argument. - put_i(func.encodings[inst].bits(), - 1, // rs1 = %x1 - 0, // no offset. - 0, // rd = %x0: no address written. - sink); -} - -fn recipe_icall(func: &Function, inst: Inst, sink: &mut CS) { - // Indirect instructions are jalr with rd=%x1. - put_i(func.encodings[inst].bits(), - func.locations[func.dfg.inst_args(inst)[0]].unwrap_reg(), - 0, // no offset. - 1, // rd = %x1: link register. - sink); -} - -fn recipe_icopy(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Unary { arg, .. } = func.dfg[inst] { - put_i(func.encodings[inst].bits(), - func.locations[arg].unwrap_reg(), - 0, - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected Unary format: {:?}", func.dfg[inst]); - } -} - /// U-type instructions. /// /// 31 11 6 @@ -236,17 +133,6 @@ fn put_u(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS) sink.put4(i); } -fn recipe_u(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::UnaryImm { imm, .. } = func.dfg[inst] { - put_u(func.encodings[inst].bits(), - imm.into(), - func.locations[func.dfg.first_result(inst)].unwrap_reg(), - sink); - } else { - panic!("Expected UnaryImm format: {:?}", func.dfg[inst]); - } -} - /// SB-type branch instructions. /// /// 31 24 19 14 11 6 @@ -280,44 +166,6 @@ fn put_sb(bits: u16, imm: i64, rs1: RegUnit, rs2: RegUnit sink.put4(i); } -fn recipe_sb(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::BranchIcmp { - destination, - ref args, - .. - } = func.dfg[inst] { - let dest = func.offsets[destination] as i64; - let disp = dest - sink.offset() as i64; - let args = &args.as_slice(&func.dfg.value_lists)[0..2]; - put_sb(func.encodings[inst].bits(), - disp, - func.locations[args[0]].unwrap_reg(), - func.locations[args[1]].unwrap_reg(), - sink); - } else { - panic!("Expected BranchIcmp format: {:?}", func.dfg[inst]); - } -} - -fn recipe_sbzero(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Branch { - destination, - ref args, - .. - } = func.dfg[inst] { - let dest = func.offsets[destination] as i64; - let disp = dest - sink.offset() as i64; - let args = &args.as_slice(&func.dfg.value_lists)[0..1]; - put_sb(func.encodings[inst].bits(), - disp, - func.locations[args[0]].unwrap_reg(), - 0, - sink); - } else { - panic!("Expected Branch format: {:?}", func.dfg[inst]); - } -} - /// UJ-type jump instructions. /// /// 31 11 6 @@ -346,31 +194,3 @@ fn put_uj(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS sink.put4(i); } - -fn recipe_uj(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Jump { destination, .. } = func.dfg[inst] { - let dest = func.offsets[destination] as i64; - let disp = dest - sink.offset() as i64; - put_uj(func.encodings[inst].bits(), disp, 0, sink); - } else { - panic!("Expected Jump format: {:?}", func.dfg[inst]); - } -} - -fn recipe_ujcall(func: &Function, inst: Inst, sink: &mut CS) { - if let InstructionData::Call { func_ref, .. } = func.dfg[inst] { - sink.reloc_func(RelocKind::Call.into(), func_ref); - // rd=%x1 is the standard link register. - put_uj(func.encodings[inst].bits(), 0, 1, sink); - } else { - panic!("Expected Call format: {:?}", func.dfg[inst]); - } -} - -fn recipe_gpsp(_func: &Function, _inst: Inst, _sink: &mut CS) { - unimplemented!(); -} - -fn recipe_gpfi(_func: &Function, _inst: Inst, _sink: &mut CS) { - unimplemented!(); -}