diff --git a/cranelift/filetests/isa/riscv/binary32.cton b/cranelift/filetests/isa/riscv/binary32.cton index f7225c234e..c2b300e07c 100644 --- a/cranelift/filetests/isa/riscv/binary32.cton +++ b/cranelift/filetests/isa/riscv/binary32.cton @@ -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 } diff --git a/cranelift/src/filetest/binemit.rs b/cranelift/src/filetest/binemit.rs index 16c2613b20..3c9fc333e6 100644 --- a/cranelift/src/filetest/binemit.rs +++ b/cranelift/src/filetest/binemit.rs @@ -25,6 +25,7 @@ pub fn subtest(parsed: &TestCommand) -> Result> { // 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:") { diff --git a/cranelift/test-all.sh b/cranelift/test-all.sh index 14b712d9b4..7c4938902f 100755 --- a/cranelift/test-all.sh +++ b/cranelift/test-all.sh @@ -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" diff --git a/lib/cretonne/meta/isa/riscv/encodings.py b/lib/cretonne/meta/isa/riscv/encodings.py index e1d186db5d..0d677c54ca 100644 --- a/lib/cretonne/meta/isa/riscv/encodings.py +++ b/lib/cretonne/meta/isa/riscv/encodings.py @@ -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 diff --git a/lib/cretonne/meta/isa/riscv/recipes.py b/lib/cretonne/meta/isa/riscv/recipes.py index ebeb9f9ee1..9fe3c6f14d 100644 --- a/lib/cretonne/meta/isa/riscv/recipes.py +++ b/lib/cretonne/meta/isa/riscv/recipes.py @@ -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=()) diff --git a/lib/cretonne/src/binemit/mod.rs b/lib/cretonne/src/binemit/mod.rs index 0b8b3475b7..ccad476f44 100644 --- a/lib/cretonne/src/binemit/mod.rs +++ b/lib/cretonne/src/binemit/mod.rs @@ -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); diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index 776d5e4bca..4eddae7b14 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -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!() + } } diff --git a/lib/cretonne/src/isa/riscv/binemit.rs b/lib/cretonne/src/isa/riscv/binemit.rs index a295a66386..888fa369ed 100644 --- a/lib/cretonne/src/isa/riscv/binemit.rs +++ b/lib/cretonne/src/isa/riscv/binemit.rs @@ -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 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(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(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(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]); + } +} diff --git a/lib/cretonne/src/isa/riscv/mod.rs b/lib/cretonne/src/isa/riscv/mod.rs index 94808bfb70..348e1af32f 100644 --- a/lib/cretonne/src/isa/riscv/mod.rs +++ b/lib/cretonne/src/isa/riscv/mod.rs @@ -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)]