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_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()
|
||||
|
||||
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
|
||||
//! 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));
|
||||
}
|
||||
|
||||
@@ -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<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.
|
||||
|
||||
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<Encoding, Legalize> {
|
||||
fn encode(&self,
|
||||
dfg: &ir::DataFlowGraph,
|
||||
inst: &ir::InstructionData)
|
||||
-> Result<Encoding, Legalize> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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<Encoding, Legalize> {
|
||||
fn encode(&self,
|
||||
dfg: &ir::DataFlowGraph,
|
||||
inst: &ir::InstructionData)
|
||||
-> Result<Encoding, Legalize> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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<Encoding, Legalize> {
|
||||
fn encode(&self,
|
||||
dfg: &ir::DataFlowGraph,
|
||||
inst: &ir::InstructionData)
|
||||
-> Result<Encoding, Legalize> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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;
|
||||
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)]
|
||||
|
||||
Reference in New Issue
Block a user