diff --git a/lib/cretonne/meta/build.py b/lib/cretonne/meta/build.py index dd7aeac5f9..08ba14a358 100644 --- a/lib/cretonne/meta/build.py +++ b/lib/cretonne/meta/build.py @@ -12,6 +12,7 @@ import gen_build_deps import gen_encoding import gen_legalizer import gen_registers +import gen_binemit parser = argparse.ArgumentParser(description='Generate sources for Cretonne.') parser.add_argument('--out-dir', help='set output directory') @@ -27,4 +28,5 @@ gen_settings.generate(isas, out_dir) gen_encoding.generate(isas, out_dir) gen_legalizer.generate(isas, out_dir) gen_registers.generate(isas, out_dir) +gen_binemit.generate(isas, out_dir) gen_build_deps.generate() diff --git a/lib/cretonne/meta/gen_binemit.py b/lib/cretonne/meta/gen_binemit.py new file mode 100644 index 0000000000..8d1954ce55 --- /dev/null +++ b/lib/cretonne/meta/gen_binemit.py @@ -0,0 +1,46 @@ +""" +Generate binary emission code for each ISA. +""" + +from __future__ import absolute_import +import srcgen + +try: + from typing import Sequence, List # noqa + from cdsl.isa import TargetISA # noqa +except ImportError: + pass + + +def gen_isa(isa, fmt): + # type: (TargetISA, srcgen.Formatter) -> None + """ + Generate Binary emission code for `isa`. + """ + fmt.doc_comment( + ''' + Emit binary machine code for `inst` for the {} ISA. + '''.format(isa.name)) + if len(isa.all_recipes) == 0: + # No encoding recipes: Emit a stub. + with fmt.indented( + 'pub fn emit_inst' + '(func: &Function, inst: Inst, _sink: &mut CS) {', '}'): + fmt.line('bad_encoding(func, inst)') + else: + with fmt.indented( + 'pub fn emit_inst' + '(func: &Function, inst: Inst, sink: &mut CS) {', '}'): + 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),') + + +def generate(isas, out_dir): + # type: (Sequence[TargetISA], str) -> None + for isa in isas: + fmt = srcgen.Formatter() + gen_isa(isa, fmt) + fmt.update_file('binemit-{}.rs'.format(isa.name), out_dir) diff --git a/lib/cretonne/src/binemit/mod.rs b/lib/cretonne/src/binemit/mod.rs index d3c97ec539..0b8b3475b7 100644 --- a/lib/cretonne/src/binemit/mod.rs +++ b/lib/cretonne/src/binemit/mod.rs @@ -3,7 +3,7 @@ //! The `binemit` module contains code for translating Cretonne's intermediate representation into //! binary machine code. -use ir::{FuncRef, JumpTable}; +use ir::{FuncRef, JumpTable, Function, Inst}; /// Relocation kinds depend on the current ISA. pub struct Reloc(u16); @@ -31,3 +31,11 @@ pub trait CodeSink { /// Add a relocation referencing a jump table. fn reloc_jt(&mut self, Reloc, JumpTable); } + +/// Report a bad encoding error. +#[inline(never)] +pub fn bad_encoding(func: &Function, inst: Inst) -> ! { + panic!("Bad encoding {} for {}", + func.encodings[inst], + func.dfg.display_inst(inst)); +} diff --git a/lib/cretonne/src/ir/valueloc.rs b/lib/cretonne/src/ir/valueloc.rs index 253c5ceb33..2d6fc644b1 100644 --- a/lib/cretonne/src/ir/valueloc.rs +++ b/lib/cretonne/src/ir/valueloc.rs @@ -25,6 +25,14 @@ impl Default for ValueLoc { } impl ValueLoc { + /// Get the register unit of this location, or panic. + pub fn unwrap_reg(self) -> RegUnit { + match self { + ValueLoc::Reg(ru) => ru, + _ => panic!("Expected register: {:?}", 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/arm32/binemit.rs b/lib/cretonne/src/isa/arm32/binemit.rs new file mode 100644 index 0000000000..dcc76331a2 --- /dev/null +++ b/lib/cretonne/src/isa/arm32/binemit.rs @@ -0,0 +1,6 @@ +//! Emitting binary ARM32 machine code. + +use binemit::{CodeSink, bad_encoding}; +use ir::{Function, Inst}; + +include!(concat!(env!("OUT_DIR"), "/binemit-arm32.rs")); diff --git a/lib/cretonne/src/isa/arm32/mod.rs b/lib/cretonne/src/isa/arm32/mod.rs index 7cc6a93358..df4f4c2570 100644 --- a/lib/cretonne/src/isa/arm32/mod.rs +++ b/lib/cretonne/src/isa/arm32/mod.rs @@ -1,14 +1,16 @@ //! ARM 32-bit Instruction Set Architecture. pub mod settings; +mod binemit; mod enc_tables; mod registers; +use binemit::CodeSink; use super::super::settings as shared_settings; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding}; use isa::Builder as IsaBuilder; use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints}; -use ir::{InstructionData, DataFlowGraph}; +use ir; #[allow(dead_code)] struct Isa { @@ -53,7 +55,10 @@ impl TargetIsa for Isa { registers::INFO.clone() } - fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result { + fn encode(&self, + dfg: &ir::DataFlowGraph, + inst: &ir::InstructionData) + -> Result { lookup_enclist(inst.ctrl_typevar(dfg), inst.opcode(), self.cpumode, @@ -74,4 +79,8 @@ impl TargetIsa for Isa { fn recipe_constraints(&self) -> &'static [RecipeConstraints] { &enc_tables::RECIPE_CONSTRAINTS } + + fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) { + binemit::emit_inst(func, inst, sink) + } } diff --git a/lib/cretonne/src/isa/arm64/binemit.rs b/lib/cretonne/src/isa/arm64/binemit.rs new file mode 100644 index 0000000000..13885307d4 --- /dev/null +++ b/lib/cretonne/src/isa/arm64/binemit.rs @@ -0,0 +1,6 @@ +//! Emitting binary ARM64 machine code. + +use binemit::{CodeSink, bad_encoding}; +use ir::{Function, Inst}; + +include!(concat!(env!("OUT_DIR"), "/binemit-arm64.rs")); diff --git a/lib/cretonne/src/isa/arm64/mod.rs b/lib/cretonne/src/isa/arm64/mod.rs index 5f253c39c2..2c11b450bb 100644 --- a/lib/cretonne/src/isa/arm64/mod.rs +++ b/lib/cretonne/src/isa/arm64/mod.rs @@ -1,14 +1,16 @@ //! ARM 64-bit Instruction Set Architecture. pub mod settings; +mod binemit; mod enc_tables; mod registers; +use binemit::CodeSink; use super::super::settings as shared_settings; use isa::enc_tables::{lookup_enclist, general_encoding}; use isa::Builder as IsaBuilder; use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints}; -use ir::{InstructionData, DataFlowGraph}; +use ir; #[allow(dead_code)] struct Isa { @@ -46,7 +48,10 @@ impl TargetIsa for Isa { registers::INFO.clone() } - fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result { + fn encode(&self, + dfg: &ir::DataFlowGraph, + inst: &ir::InstructionData) + -> Result { lookup_enclist(inst.ctrl_typevar(dfg), inst.opcode(), &enc_tables::LEVEL1_A64[..], @@ -67,4 +72,8 @@ impl TargetIsa for Isa { fn recipe_constraints(&self) -> &'static [RecipeConstraints] { &enc_tables::RECIPE_CONSTRAINTS } + + fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) { + binemit::emit_inst(func, inst, sink) + } } diff --git a/lib/cretonne/src/isa/intel/binemit.rs b/lib/cretonne/src/isa/intel/binemit.rs new file mode 100644 index 0000000000..8870abd8f1 --- /dev/null +++ b/lib/cretonne/src/isa/intel/binemit.rs @@ -0,0 +1,6 @@ +//! Emitting binary Intel machine code. + +use binemit::{CodeSink, bad_encoding}; +use ir::{Function, Inst}; + +include!(concat!(env!("OUT_DIR"), "/binemit-intel.rs")); diff --git a/lib/cretonne/src/isa/intel/mod.rs b/lib/cretonne/src/isa/intel/mod.rs index 4a5ea8201a..46aa101ce0 100644 --- a/lib/cretonne/src/isa/intel/mod.rs +++ b/lib/cretonne/src/isa/intel/mod.rs @@ -1,14 +1,16 @@ //! Intel Instruction Set Architectures. pub mod settings; +mod binemit; mod enc_tables; mod registers; +use binemit::CodeSink; use super::super::settings as shared_settings; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding}; use isa::Builder as IsaBuilder; use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints}; -use ir::{InstructionData, DataFlowGraph}; +use ir; #[allow(dead_code)] struct Isa { @@ -53,7 +55,10 @@ impl TargetIsa for Isa { registers::INFO.clone() } - fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result { + fn encode(&self, + dfg: &ir::DataFlowGraph, + inst: &ir::InstructionData) + -> Result { lookup_enclist(inst.ctrl_typevar(dfg), inst.opcode(), self.cpumode, @@ -74,4 +79,8 @@ impl TargetIsa for Isa { fn recipe_constraints(&self) -> &'static [RecipeConstraints] { &enc_tables::RECIPE_CONSTRAINTS } + + fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) { + binemit::emit_inst(func, inst, sink) + } } diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index 37684addb1..776d5e4bca 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -44,8 +44,9 @@ pub use isa::encoding::Encoding; pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex}; pub use isa::constraints::{RecipeConstraints, OperandConstraint, ConstraintKind}; +use binemit::CodeSink; use settings; -use ir::{InstructionData, DataFlowGraph, Signature}; +use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature}; pub mod riscv; pub mod intel; @@ -181,4 +182,10 @@ pub trait TargetIsa { fn legalize_signature(&self, _sig: &mut Signature) { unimplemented!() } + + /// Emit binary machine code for a single instruction into the `sink` trait object. + /// + /// Note that this will call `put*` methods on the trait object via its vtable which is not the + /// fastest way of emitting code. + fn emit_inst(&self, func: &Function, inst: Inst, sink: &mut CodeSink); } diff --git a/lib/cretonne/src/isa/riscv/binemit.rs b/lib/cretonne/src/isa/riscv/binemit.rs new file mode 100644 index 0000000000..e7dcc6021f --- /dev/null +++ b/lib/cretonne/src/isa/riscv/binemit.rs @@ -0,0 +1,52 @@ +//! Emitting binary RISC-V machine code. + +use binemit::{CodeSink, bad_encoding}; +use ir::{Function, Inst, InstructionData}; + +include!(concat!(env!("OUT_DIR"), "/binemit-riscv.rs")); + +/// R-type instructions. +/// +/// 31 24 19 14 11 6 +/// funct7 rs2 rs1 funct3 rd opcode +/// 25 20 15 12 7 0 +/// +/// Encoding bits: `opcode[6:2] | (funct3 << 5) | (funct7 << 8)`. +fn recipe_r(func: &Function, inst: Inst, sink: &mut CS) { + if let InstructionData::Binary { args, .. } = func.dfg[inst] { + let bits = func.encodings[inst].bits(); + let rs1 = func.locations[args[0]].unwrap_reg(); + let rs2 = func.locations[args[1]].unwrap_reg(); + let rd = func.locations[func.dfg.first_result(inst)].unwrap_reg(); + + // 0-6: opcode + let mut i = 0x3; + i |= (bits as u32 & 0x1f) << 2; + // 7-11: rd + i |= (rd as u32 & 0x1f) << 7; + // 12-14: funct3 + i |= ((bits as u32 >> 5) & 0x7) << 12; + // 15-19: rs1 + i |= (rs1 as u32 & 0x1f) << 15; + // 20-24: rs1 + i |= (rs2 as u32 & 0x1f) << 20; + // 25-31: funct7 + i |= ((bits as u32 >> 8) & 0x7f) << 25; + + sink.put4(i); + } else { + panic!("Expected Binary format: {:?}", func.dfg[inst]); + } +} + +fn recipe_rshamt(_func: &Function, _inst: Inst, _sink: &mut CS) { + unimplemented!() +} + +fn recipe_i(_func: &Function, _inst: Inst, _sink: &mut CS) { + unimplemented!() +} + +fn recipe_iret(_func: &Function, _inst: Inst, _sink: &mut CS) { + unimplemented!() +} diff --git a/lib/cretonne/src/isa/riscv/mod.rs b/lib/cretonne/src/isa/riscv/mod.rs index ce04fe70b6..94808bfb70 100644 --- a/lib/cretonne/src/isa/riscv/mod.rs +++ b/lib/cretonne/src/isa/riscv/mod.rs @@ -2,14 +2,16 @@ pub mod settings; mod abi; +mod binemit; mod enc_tables; mod registers; use super::super::settings as shared_settings; +use binemit::CodeSink; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding}; use isa::Builder as IsaBuilder; use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints}; -use ir::{InstructionData, DataFlowGraph, Signature}; +use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature}; #[allow(dead_code)] struct Isa { @@ -80,6 +82,10 @@ impl TargetIsa for Isa { // We can pass in `self.isa_flags` too, if we need it. abi::legalize_signature(sig, &self.shared_flags) } + + fn emit_inst(&self, func: &Function, inst: Inst, sink: &mut CodeSink) { + binemit::emit_inst(func, inst, sink) + } } #[cfg(test)]