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:
@@ -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()
|
||||||
|
|||||||
46
lib/cretonne/meta/gen_binemit.py
Normal file
46
lib/cretonne/meta/gen_binemit.py
Normal 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)
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
6
lib/cretonne/src/isa/arm32/binemit.rs
Normal file
6
lib/cretonne/src/isa/arm32/binemit.rs
Normal 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"));
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
lib/cretonne/src/isa/arm64/binemit.rs
Normal file
6
lib/cretonne/src/isa/arm64/binemit.rs
Normal 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"));
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
lib/cretonne/src/isa/intel/binemit.rs
Normal file
6
lib/cretonne/src/isa/intel/binemit.rs
Normal 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"));
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
52
lib/cretonne/src/isa/riscv/binemit.rs
Normal file
52
lib/cretonne/src/isa/riscv/binemit.rs
Normal 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!()
|
||||||
|
}
|
||||||
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user