Generate binemit::emit_inst() functions.

Use the meta language encoding recipes to generate an emit_inst()
function for each ISA. The generated calls into recipe_*() functions
that must be implemented by hand.

Implement recipe_*() functions for the RISC-V recipes.

Add the TargetIsa::emit_inst() entry point which emits an instruction to
a CodeSink trait object.
This commit is contained in:
Jakob Stoklund Olesen
2017-03-27 16:11:54 -07:00
parent 19710af5b7
commit 0619d6f827
13 changed files with 183 additions and 9 deletions

View File

@@ -12,6 +12,7 @@ import gen_build_deps
import gen_encoding import gen_encoding
import gen_legalizer import gen_legalizer
import gen_registers import gen_registers
import gen_binemit
parser = argparse.ArgumentParser(description='Generate sources for Cretonne.') parser = argparse.ArgumentParser(description='Generate sources for Cretonne.')
parser.add_argument('--out-dir', help='set output directory') 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_encoding.generate(isas, out_dir)
gen_legalizer.generate(isas, out_dir) gen_legalizer.generate(isas, out_dir)
gen_registers.generate(isas, out_dir) gen_registers.generate(isas, out_dir)
gen_binemit.generate(isas, out_dir)
gen_build_deps.generate() gen_build_deps.generate()

View File

@@ -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<CS: CodeSink + ?Sized>'
'(func: &Function, inst: Inst, _sink: &mut CS) {', '}'):
fmt.line('bad_encoding(func, inst)')
else:
with fmt.indented(
'pub fn emit_inst<CS: CodeSink + ?Sized>'
'(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)

View File

@@ -3,7 +3,7 @@
//! The `binemit` module contains code for translating Cretonne's intermediate representation into //! The `binemit` module contains code for translating Cretonne's intermediate representation into
//! binary machine code. //! binary machine code.
use ir::{FuncRef, JumpTable}; use ir::{FuncRef, JumpTable, Function, Inst};
/// Relocation kinds depend on the current ISA. /// Relocation kinds depend on the current ISA.
pub struct Reloc(u16); pub struct Reloc(u16);
@@ -31,3 +31,11 @@ pub trait CodeSink {
/// Add a relocation referencing a jump table. /// Add a relocation referencing a jump table.
fn reloc_jt(&mut self, Reloc, JumpTable); 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));
}

View File

@@ -25,6 +25,14 @@ impl Default for ValueLoc {
} }
impl 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 /// Return an object that can display this value location, using the register info from the
/// target ISA. /// target ISA.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(self, regs: R) -> DisplayValueLoc<'a> { pub fn display<'a, R: Into<Option<&'a RegInfo>>>(self, regs: R) -> DisplayValueLoc<'a> {

View File

@@ -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"));

View File

@@ -1,14 +1,16 @@
//! ARM 32-bit Instruction Set Architecture. //! ARM 32-bit Instruction Set Architecture.
pub mod settings; pub mod settings;
mod binemit;
mod enc_tables; mod enc_tables;
mod registers; mod registers;
use binemit::CodeSink;
use super::super::settings as shared_settings; use super::super::settings as shared_settings;
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding}; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
use isa::Builder as IsaBuilder; use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints}; use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints};
use ir::{InstructionData, DataFlowGraph}; use ir;
#[allow(dead_code)] #[allow(dead_code)]
struct Isa { struct Isa {
@@ -53,7 +55,10 @@ impl TargetIsa for Isa {
registers::INFO.clone() registers::INFO.clone()
} }
fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize> { fn encode(&self,
dfg: &ir::DataFlowGraph,
inst: &ir::InstructionData)
-> Result<Encoding, Legalize> {
lookup_enclist(inst.ctrl_typevar(dfg), lookup_enclist(inst.ctrl_typevar(dfg),
inst.opcode(), inst.opcode(),
self.cpumode, self.cpumode,
@@ -74,4 +79,8 @@ impl TargetIsa for Isa {
fn recipe_constraints(&self) -> &'static [RecipeConstraints] { fn recipe_constraints(&self) -> &'static [RecipeConstraints] {
&enc_tables::RECIPE_CONSTRAINTS &enc_tables::RECIPE_CONSTRAINTS
} }
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) {
binemit::emit_inst(func, inst, sink)
}
} }

View File

@@ -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"));

View File

@@ -1,14 +1,16 @@
//! ARM 64-bit Instruction Set Architecture. //! ARM 64-bit Instruction Set Architecture.
pub mod settings; pub mod settings;
mod binemit;
mod enc_tables; mod enc_tables;
mod registers; mod registers;
use binemit::CodeSink;
use super::super::settings as shared_settings; use super::super::settings as shared_settings;
use isa::enc_tables::{lookup_enclist, general_encoding}; use isa::enc_tables::{lookup_enclist, general_encoding};
use isa::Builder as IsaBuilder; use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints}; use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints};
use ir::{InstructionData, DataFlowGraph}; use ir;
#[allow(dead_code)] #[allow(dead_code)]
struct Isa { struct Isa {
@@ -46,7 +48,10 @@ impl TargetIsa for Isa {
registers::INFO.clone() registers::INFO.clone()
} }
fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize> { fn encode(&self,
dfg: &ir::DataFlowGraph,
inst: &ir::InstructionData)
-> Result<Encoding, Legalize> {
lookup_enclist(inst.ctrl_typevar(dfg), lookup_enclist(inst.ctrl_typevar(dfg),
inst.opcode(), inst.opcode(),
&enc_tables::LEVEL1_A64[..], &enc_tables::LEVEL1_A64[..],
@@ -67,4 +72,8 @@ impl TargetIsa for Isa {
fn recipe_constraints(&self) -> &'static [RecipeConstraints] { fn recipe_constraints(&self) -> &'static [RecipeConstraints] {
&enc_tables::RECIPE_CONSTRAINTS &enc_tables::RECIPE_CONSTRAINTS
} }
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) {
binemit::emit_inst(func, inst, sink)
}
} }

View File

@@ -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"));

View File

@@ -1,14 +1,16 @@
//! Intel Instruction Set Architectures. //! Intel Instruction Set Architectures.
pub mod settings; pub mod settings;
mod binemit;
mod enc_tables; mod enc_tables;
mod registers; mod registers;
use binemit::CodeSink;
use super::super::settings as shared_settings; use super::super::settings as shared_settings;
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding}; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
use isa::Builder as IsaBuilder; use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints}; use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints};
use ir::{InstructionData, DataFlowGraph}; use ir;
#[allow(dead_code)] #[allow(dead_code)]
struct Isa { struct Isa {
@@ -53,7 +55,10 @@ impl TargetIsa for Isa {
registers::INFO.clone() registers::INFO.clone()
} }
fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize> { fn encode(&self,
dfg: &ir::DataFlowGraph,
inst: &ir::InstructionData)
-> Result<Encoding, Legalize> {
lookup_enclist(inst.ctrl_typevar(dfg), lookup_enclist(inst.ctrl_typevar(dfg),
inst.opcode(), inst.opcode(),
self.cpumode, self.cpumode,
@@ -74,4 +79,8 @@ impl TargetIsa for Isa {
fn recipe_constraints(&self) -> &'static [RecipeConstraints] { fn recipe_constraints(&self) -> &'static [RecipeConstraints] {
&enc_tables::RECIPE_CONSTRAINTS &enc_tables::RECIPE_CONSTRAINTS
} }
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) {
binemit::emit_inst(func, inst, sink)
}
} }

View File

@@ -44,8 +44,9 @@ pub use isa::encoding::Encoding;
pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex}; pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex};
pub use isa::constraints::{RecipeConstraints, OperandConstraint, ConstraintKind}; pub use isa::constraints::{RecipeConstraints, OperandConstraint, ConstraintKind};
use binemit::CodeSink;
use settings; use settings;
use ir::{InstructionData, DataFlowGraph, Signature}; use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature};
pub mod riscv; pub mod riscv;
pub mod intel; pub mod intel;
@@ -181,4 +182,10 @@ pub trait TargetIsa {
fn legalize_signature(&self, _sig: &mut Signature) { fn legalize_signature(&self, _sig: &mut Signature) {
unimplemented!() 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);
} }

View File

@@ -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<CS: CodeSink + ?Sized>(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<CS: CodeSink + ?Sized>(_func: &Function, _inst: Inst, _sink: &mut CS) {
unimplemented!()
}
fn recipe_i<CS: CodeSink + ?Sized>(_func: &Function, _inst: Inst, _sink: &mut CS) {
unimplemented!()
}
fn recipe_iret<CS: CodeSink + ?Sized>(_func: &Function, _inst: Inst, _sink: &mut CS) {
unimplemented!()
}

View File

@@ -2,14 +2,16 @@
pub mod settings; pub mod settings;
mod abi; mod abi;
mod binemit;
mod enc_tables; mod enc_tables;
mod registers; mod registers;
use super::super::settings as shared_settings; 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::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
use isa::Builder as IsaBuilder; use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints}; use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints};
use ir::{InstructionData, DataFlowGraph, Signature}; use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature};
#[allow(dead_code)] #[allow(dead_code)]
struct Isa { struct Isa {
@@ -80,6 +82,10 @@ impl TargetIsa for Isa {
// We can pass in `self.isa_flags` too, if we need it. // We can pass in `self.isa_flags` too, if we need it.
abi::legalize_signature(sig, &self.shared_flags) 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)] #[cfg(test)]