Add conditional branch encodings for RISC-V.

Not all br_icmp opcodes are present in the ISA. The missing ones can be
reached by commuting operands.

Don't attempt to encode EBB offsets yet. For now just emit an EBB
relocation for the branch instruction.
This commit is contained in:
Jakob Stoklund Olesen
2017-04-03 15:07:08 -07:00
parent 1b6a6f4e48
commit 479ff156c1
9 changed files with 124 additions and 12 deletions

View File

@@ -75,5 +75,21 @@ ebb0:
[-,%x7] v140 = iconst.i32 0x12345000 ; bin: 123453b7
[-,%x16] v141 = iconst.i32 0xffffffff_fedcb000 ; bin: fedcb837
; Control Transfer Instructions
; beq
br_icmp eq, v1, v2, ebb0 ; bin: Branch(ebb0) 01550063
; bne
br_icmp ne, v1, v2, ebb0 ; bin: Branch(ebb0) 01551063
; blt
br_icmp slt, v1, v2, ebb0 ; bin: Branch(ebb0) 01554063
; bge
br_icmp sge, v1, v2, ebb0 ; bin: Branch(ebb0) 01555063
; bltu
br_icmp ult, v1, v2, ebb0 ; bin: Branch(ebb0) 01556063
; bgeu
br_icmp uge, v1, v2, ebb0 ; bin: Branch(ebb0) 01557063
return
}

View File

@@ -25,6 +25,7 @@ pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
// Code sink that generates text.
struct TextSink {
rnames: &'static [&'static str],
text: String,
}
@@ -45,12 +46,16 @@ impl binemit::CodeSink for TextSink {
write!(self.text, "{:016x} ", x).unwrap();
}
fn reloc_func(&mut self, _: binemit::Reloc, _: ir::FuncRef) {
unimplemented!()
fn reloc_ebb(&mut self, reloc: binemit::Reloc, ebb: ir::Ebb) {
write!(self.text, "{}({}) ", self.rnames[reloc.0 as usize], ebb).unwrap();
}
fn reloc_jt(&mut self, _: binemit::Reloc, _: ir::JumpTable) {
unimplemented!()
fn reloc_func(&mut self, reloc: binemit::Reloc, fref: ir::FuncRef) {
write!(self.text, "{}({}) ", self.rnames[reloc.0 as usize], fref).unwrap();
}
fn reloc_jt(&mut self, reloc: binemit::Reloc, jt: ir::JumpTable) {
write!(self.text, "{}({}) ", self.rnames[reloc.0 as usize], jt).unwrap();
}
}
@@ -73,7 +78,10 @@ impl SubTest for TestBinEmit {
// value locations. The current error reporting is just crashing...
let mut func = func.into_owned();
let mut sink = TextSink { text: String::new() };
let mut sink = TextSink {
rnames: isa.reloc_names(),
text: String::new(),
};
for comment in &context.details.comments {
if let Some(want) = match_directive(comment.text, "bin:") {

View File

@@ -56,7 +56,7 @@ fi
if [ -n "$needcheck" ]; then
banner $(python --version 2>&1)
$topdir/lib/cretonne/meta/check.sh
touch $tsfile
touch $tsfile || echo no target directory
fi
PKGS="cretonne cretonne-reader cretonne-tools filecheck"

View File

@@ -5,14 +5,16 @@ from __future__ import absolute_import
from base import instructions as base
from base.immediates import intcc
from .defs import RV32, RV64
from .recipes import OPIMM, OPIMM32, OP, OP32, LUI
from .recipes import JALR, R, Rshamt, Ricmp, I, Iicmp, Iret, U
from .recipes import OPIMM, OPIMM32, OP, OP32, LUI, BRANCH
from .recipes import JALR, R, Rshamt, Ricmp, I, Iicmp, Iret, U, SB
from .settings import use_m
from cdsl.ast import Var
# Dummies for instruction predicates.
x = Var('x')
y = Var('y')
dest = Var('dest')
args = Var('args')
# Basic arithmetic binary instructions are encoded in an R-type instruction.
for inst, inst_imm, f3, f7 in [
@@ -79,6 +81,18 @@ RV64.enc(base.imul.i32, R, OP32(0b000, 0b0000001), isap=use_m)
# Control flow.
# Conditional branches.
for cond, f3 in [
(intcc.eq, 0b000),
(intcc.ne, 0b001),
(intcc.slt, 0b100),
(intcc.sge, 0b101),
(intcc.ult, 0b110),
(intcc.uge, 0b111)
]:
RV32.enc(base.br_icmp.i32(cond, x, y, dest, args), SB, BRANCH(f3))
RV64.enc(base.br_icmp.i64(cond, x, y, dest, args), SB, BRANCH(f3))
# Returns are a special case of JALR.
# Note: Return stack predictors will only recognize this as a return when the
# return address is provided in `x1`. We may want a special encoding to enforce

View File

@@ -12,7 +12,7 @@ from __future__ import absolute_import
from cdsl.isa import EncRecipe
from cdsl.predicates import IsSignedInt
from base.formats import Binary, BinaryImm, MultiAry, IntCompare, IntCompareImm
from base.formats import UnaryImm
from base.formats import UnaryImm, BranchIcmp
from .registers import GPR
# The low 7 bits of a RISC-V instruction is the base opcode. All 32-bit
@@ -111,3 +111,8 @@ Iret = EncRecipe('Iret', MultiAry, ins=GPR, outs=())
U = EncRecipe(
'U', UnaryImm, ins=(), outs=GPR,
instp=IsSignedInt(UnaryImm.imm, 32, 12))
# SB-type branch instructions.
# TODO: These instructions have a +/- 4 KB branch range. How to encode that
# constraint?
SB = EncRecipe('SB', BranchIcmp, ins=(GPR, GPR), outs=())

View File

@@ -3,10 +3,10 @@
//! The `binemit` module contains code for translating Cretonne's intermediate representation into
//! binary machine code.
use ir::{FuncRef, JumpTable, Function, Inst};
use ir::{Ebb, FuncRef, JumpTable, Function, Inst};
/// Relocation kinds depend on the current ISA.
pub struct Reloc(u16);
pub struct Reloc(pub u16);
/// Abstract interface for adding bytes to the code segment.
///
@@ -25,6 +25,9 @@ pub trait CodeSink {
/// Add 8 bytes to the code section.
fn put8(&mut self, u64);
/// Add a relocation referencing an EBB at the current offset.
fn reloc_ebb(&mut self, Reloc, Ebb);
/// Add a relocation referencing an external function at the current offset.
fn reloc_func(&mut self, Reloc, FuncRef);

View File

@@ -188,4 +188,12 @@ pub trait TargetIsa {
/// 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);
/// Get a static array of names associated with relocations in this ISA.
///
/// This array can be indexed by the contents of `binemit::Reloc` objects passed to a
/// `CodeSink`.
fn reloc_names(&self) -> &'static [&'static str] {
unimplemented!()
}
}

View File

@@ -1,11 +1,25 @@
//! Emitting binary RISC-V machine code.
use binemit::{CodeSink, bad_encoding};
use binemit::{CodeSink, Reloc, bad_encoding};
use ir::{Function, Inst, InstructionData};
use isa::RegUnit;
include!(concat!(env!("OUT_DIR"), "/binemit-riscv.rs"));
/// RISC-V relocation kinds.
pub enum RelocKind {
/// A conditional (SB-type) branch to an EBB.
Branch,
}
pub static RELOC_NAMES: [&'static str; 1] = ["Branch"];
impl Into<Reloc> for RelocKind {
fn into(self) -> Reloc {
Reloc(self as u16)
}
}
/// R-type instructions.
///
/// 31 24 19 14 11 6
@@ -192,3 +206,43 @@ fn recipe_u<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
panic!("Expected UnaryImm format: {:?}", func.dfg[inst]);
}
}
/// SB-type branch instructions.
///
/// 31 24 19 14 11 6
/// imm rs2 rs1 funct3 imm opcode
/// 25 20 15 12 7 0
///
/// The imm bits are not encoded by this function. They encode the relative distance to the
/// destination block, handled by a relocation.
///
/// Encoding bits: `opcode[6:2] | (funct3 << 5)`
fn put_sb<CS: CodeSink + ?Sized>(bits: u16, rs1: RegUnit, rs2: RegUnit, sink: &mut CS) {
let bits = bits as u32;
let opcode5 = bits & 0x1f;
let funct3 = (bits >> 5) & 0x7;
let rs1 = rs1 as u32 & 0x1f;
let rs2 = rs2 as u32 & 0x1f;
// 0-6: opcode
let mut i = 0x3;
i |= opcode5 << 2;
i |= funct3 << 12;
i |= rs1 << 15;
i |= rs2 << 20;
sink.put4(i);
}
fn recipe_sb<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::BranchIcmp { destination, ref args, .. } = func.dfg[inst] {
let args = &args.as_slice(&func.dfg.value_lists)[0..2];
sink.reloc_ebb(RelocKind::Branch.into(), destination);
put_sb(func.encodings[inst].bits(),
func.locations[args[0]].unwrap_reg(),
func.locations[args[1]].unwrap_reg(),
sink);
} else {
panic!("Expected BranchIcmp format: {:?}", func.dfg[inst]);
}
}

View File

@@ -86,6 +86,10 @@ impl TargetIsa for Isa {
fn emit_inst(&self, func: &Function, inst: Inst, sink: &mut CodeSink) {
binemit::emit_inst(func, inst, sink)
}
fn reloc_names(&self) -> &'static [&'static str] {
&binemit::RELOC_NAMES
}
}
#[cfg(test)]