summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cranelift-codegen/src/isa/riscv
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/cranelift-codegen/src/isa/riscv')
-rw-r--r--third_party/rust/cranelift-codegen/src/isa/riscv/abi.rs149
-rw-r--r--third_party/rust/cranelift-codegen/src/isa/riscv/binemit.rs182
-rw-r--r--third_party/rust/cranelift-codegen/src/isa/riscv/enc_tables.rs18
-rw-r--r--third_party/rust/cranelift-codegen/src/isa/riscv/mod.rs295
-rw-r--r--third_party/rust/cranelift-codegen/src/isa/riscv/registers.rs50
-rw-r--r--third_party/rust/cranelift-codegen/src/isa/riscv/settings.rs56
6 files changed, 750 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen/src/isa/riscv/abi.rs b/third_party/rust/cranelift-codegen/src/isa/riscv/abi.rs
new file mode 100644
index 0000000000..44c5f36afe
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/isa/riscv/abi.rs
@@ -0,0 +1,149 @@
+//! RISC-V ABI implementation.
+//!
+//! This module implements the RISC-V calling convention through the primary `legalize_signature()`
+//! entry point.
+//!
+//! This doesn't support the soft-float ABI at the moment.
+
+use super::registers::{FPR, GPR};
+use super::settings;
+use crate::abi::{legalize_args, ArgAction, ArgAssigner, ValueConversion};
+use crate::ir::{self, AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, Type};
+use crate::isa::RegClass;
+use crate::regalloc::RegisterSet;
+use alloc::borrow::Cow;
+use core::i32;
+use target_lexicon::Triple;
+
+struct Args {
+ pointer_bits: u8,
+ pointer_bytes: u8,
+ pointer_type: Type,
+ regs: u32,
+ reg_limit: u32,
+ offset: u32,
+}
+
+impl Args {
+ fn new(bits: u8, enable_e: bool) -> Self {
+ Self {
+ pointer_bits: bits,
+ pointer_bytes: bits / 8,
+ pointer_type: Type::int(u16::from(bits)).unwrap(),
+ regs: 0,
+ reg_limit: if enable_e { 6 } else { 8 },
+ offset: 0,
+ }
+ }
+}
+
+impl ArgAssigner for Args {
+ fn assign(&mut self, arg: &AbiParam) -> ArgAction {
+ fn align(value: u32, to: u32) -> u32 {
+ (value + to - 1) & !(to - 1)
+ }
+
+ let ty = arg.value_type;
+
+ // Check for a legal type.
+ // RISC-V doesn't have SIMD at all, so break all vectors down.
+ if ty.is_vector() {
+ return ValueConversion::VectorSplit.into();
+ }
+
+ // Large integers and booleans are broken down to fit in a register.
+ if !ty.is_float() && ty.bits() > u16::from(self.pointer_bits) {
+ // Align registers and stack to a multiple of two pointers.
+ self.regs = align(self.regs, 2);
+ self.offset = align(self.offset, 2 * u32::from(self.pointer_bytes));
+ return ValueConversion::IntSplit.into();
+ }
+
+ // Small integers are extended to the size of a pointer register.
+ if ty.is_int() && ty.bits() < u16::from(self.pointer_bits) {
+ match arg.extension {
+ ArgumentExtension::None => {}
+ ArgumentExtension::Uext => return ValueConversion::Uext(self.pointer_type).into(),
+ ArgumentExtension::Sext => return ValueConversion::Sext(self.pointer_type).into(),
+ }
+ }
+
+ if self.regs < self.reg_limit {
+ // Assign to a register.
+ let reg = if ty.is_float() {
+ FPR.unit(10 + self.regs as usize)
+ } else {
+ GPR.unit(10 + self.regs as usize)
+ };
+ self.regs += 1;
+ ArgumentLoc::Reg(reg).into()
+ } else {
+ // Assign a stack location.
+ let loc = ArgumentLoc::Stack(self.offset as i32);
+ self.offset += u32::from(self.pointer_bytes);
+ debug_assert!(self.offset <= i32::MAX as u32);
+ loc.into()
+ }
+ }
+}
+
+/// Legalize `sig` for RISC-V.
+pub fn legalize_signature(
+ sig: &mut Cow<ir::Signature>,
+ triple: &Triple,
+ isa_flags: &settings::Flags,
+ current: bool,
+) {
+ let bits = triple.pointer_width().unwrap().bits();
+
+ let mut args = Args::new(bits, isa_flags.enable_e());
+ if let Some(new_params) = legalize_args(&sig.params, &mut args) {
+ sig.to_mut().params = new_params;
+ }
+
+ let mut rets = Args::new(bits, isa_flags.enable_e());
+ if let Some(new_returns) = legalize_args(&sig.returns, &mut rets) {
+ sig.to_mut().returns = new_returns;
+ }
+
+ if current {
+ let ptr = Type::int(u16::from(bits)).unwrap();
+
+ // Add the link register as an argument and return value.
+ //
+ // The `jalr` instruction implementing a return can technically accept the return address
+ // in any register, but a micro-architecture with a return address predictor will only
+ // recognize it as a return if the address is in `x1`.
+ let link = AbiParam::special_reg(ptr, ArgumentPurpose::Link, GPR.unit(1));
+ sig.to_mut().params.push(link);
+ sig.to_mut().returns.push(link);
+ }
+}
+
+/// Get register class for a type appearing in a legalized signature.
+pub fn regclass_for_abi_type(ty: Type) -> RegClass {
+ if ty.is_float() {
+ FPR
+ } else {
+ GPR
+ }
+}
+
+pub fn allocatable_registers(_func: &ir::Function, isa_flags: &settings::Flags) -> RegisterSet {
+ let mut regs = RegisterSet::new();
+ regs.take(GPR, GPR.unit(0)); // Hard-wired 0.
+ // %x1 is the link register which is available for allocation.
+ regs.take(GPR, GPR.unit(2)); // Stack pointer.
+ regs.take(GPR, GPR.unit(3)); // Global pointer.
+ regs.take(GPR, GPR.unit(4)); // Thread pointer.
+ // TODO: %x8 is the frame pointer. Reserve it?
+
+ // Remove %x16 and up for RV32E.
+ if isa_flags.enable_e() {
+ for u in 16..32 {
+ regs.take(GPR, GPR.unit(u));
+ }
+ }
+
+ regs
+}
diff --git a/third_party/rust/cranelift-codegen/src/isa/riscv/binemit.rs b/third_party/rust/cranelift-codegen/src/isa/riscv/binemit.rs
new file mode 100644
index 0000000000..a1d2b82e12
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/isa/riscv/binemit.rs
@@ -0,0 +1,182 @@
+//! Emitting binary RISC-V machine code.
+
+use crate::binemit::{bad_encoding, CodeSink, Reloc};
+use crate::ir::{Function, Inst, InstructionData};
+use crate::isa::{RegUnit, StackBaseMask, StackRef, TargetIsa};
+use crate::predicates::is_signed_int;
+use crate::regalloc::RegDiversions;
+use core::u32;
+
+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 put_r<CS: CodeSink + ?Sized>(bits: u16, rs1: RegUnit, rs2: RegUnit, rd: RegUnit, sink: &mut CS) {
+ let bits = u32::from(bits);
+ let opcode5 = bits & 0x1f;
+ let funct3 = (bits >> 5) & 0x7;
+ let funct7 = (bits >> 8) & 0x7f;
+ let rs1 = u32::from(rs1) & 0x1f;
+ let rs2 = u32::from(rs2) & 0x1f;
+ let rd = u32::from(rd) & 0x1f;
+
+ // 0-6: opcode
+ let mut i = 0x3;
+ i |= opcode5 << 2;
+ i |= rd << 7;
+ i |= funct3 << 12;
+ i |= rs1 << 15;
+ i |= rs2 << 20;
+ i |= funct7 << 25;
+
+ sink.put4(i);
+}
+
+/// R-type instructions with a shift amount instead of rs2.
+///
+/// 31 25 19 14 11 6
+/// funct7 shamt rs1 funct3 rd opcode
+/// 25 20 15 12 7 0
+///
+/// Both funct7 and shamt contribute to bit 25. In RV64, shamt uses it for shifts > 31.
+///
+/// Encoding bits: `opcode[6:2] | (funct3 << 5) | (funct7 << 8)`.
+fn put_rshamt<CS: CodeSink + ?Sized>(
+ bits: u16,
+ rs1: RegUnit,
+ shamt: i64,
+ rd: RegUnit,
+ sink: &mut CS,
+) {
+ let bits = u32::from(bits);
+ let opcode5 = bits & 0x1f;
+ let funct3 = (bits >> 5) & 0x7;
+ let funct7 = (bits >> 8) & 0x7f;
+ let rs1 = u32::from(rs1) & 0x1f;
+ let shamt = shamt as u32 & 0x3f;
+ let rd = u32::from(rd) & 0x1f;
+
+ // 0-6: opcode
+ let mut i = 0x3;
+ i |= opcode5 << 2;
+ i |= rd << 7;
+ i |= funct3 << 12;
+ i |= rs1 << 15;
+ i |= shamt << 20;
+ i |= funct7 << 25;
+
+ sink.put4(i);
+}
+
+/// I-type instructions.
+///
+/// 31 19 14 11 6
+/// imm rs1 funct3 rd opcode
+/// 20 15 12 7 0
+///
+/// Encoding bits: `opcode[6:2] | (funct3 << 5)`
+fn put_i<CS: CodeSink + ?Sized>(bits: u16, rs1: RegUnit, imm: i64, rd: RegUnit, sink: &mut CS) {
+ let bits = u32::from(bits);
+ let opcode5 = bits & 0x1f;
+ let funct3 = (bits >> 5) & 0x7;
+ let rs1 = u32::from(rs1) & 0x1f;
+ let rd = u32::from(rd) & 0x1f;
+
+ // 0-6: opcode
+ let mut i = 0x3;
+ i |= opcode5 << 2;
+ i |= rd << 7;
+ i |= funct3 << 12;
+ i |= rs1 << 15;
+ i |= (imm << 20) as u32;
+
+ sink.put4(i);
+}
+
+/// U-type instructions.
+///
+/// 31 11 6
+/// imm rd opcode
+/// 12 7 0
+///
+/// Encoding bits: `opcode[6:2] | (funct3 << 5)`
+fn put_u<CS: CodeSink + ?Sized>(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS) {
+ let bits = u32::from(bits);
+ let opcode5 = bits & 0x1f;
+ let rd = u32::from(rd) & 0x1f;
+
+ // 0-6: opcode
+ let mut i = 0x3;
+ i |= opcode5 << 2;
+ i |= rd << 7;
+ i |= imm as u32 & 0xfffff000;
+
+ sink.put4(i);
+}
+
+/// SB-type branch instructions.
+///
+/// 31 24 19 14 11 6
+/// imm rs2 rs1 funct3 imm opcode
+/// 25 20 15 12 7 0
+///
+/// Encoding bits: `opcode[6:2] | (funct3 << 5)`
+fn put_sb<CS: CodeSink + ?Sized>(bits: u16, imm: i64, rs1: RegUnit, rs2: RegUnit, sink: &mut CS) {
+ let bits = u32::from(bits);
+ let opcode5 = bits & 0x1f;
+ let funct3 = (bits >> 5) & 0x7;
+ let rs1 = u32::from(rs1) & 0x1f;
+ let rs2 = u32::from(rs2) & 0x1f;
+
+ debug_assert!(is_signed_int(imm, 13, 1), "SB out of range {:#x}", imm);
+ let imm = imm as u32;
+
+ // 0-6: opcode
+ let mut i = 0x3;
+ i |= opcode5 << 2;
+ i |= funct3 << 12;
+ i |= rs1 << 15;
+ i |= rs2 << 20;
+
+ // The displacement is completely hashed up.
+ i |= ((imm >> 11) & 0x1) << 7;
+ i |= ((imm >> 1) & 0xf) << 8;
+ i |= ((imm >> 5) & 0x3f) << 25;
+ i |= ((imm >> 12) & 0x1) << 31;
+
+ sink.put4(i);
+}
+
+/// UJ-type jump instructions.
+///
+/// 31 11 6
+/// imm rd opcode
+/// 12 7 0
+///
+/// Encoding bits: `opcode[6:2]`
+fn put_uj<CS: CodeSink + ?Sized>(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS) {
+ let bits = u32::from(bits);
+ let opcode5 = bits & 0x1f;
+ let rd = u32::from(rd) & 0x1f;
+
+ debug_assert!(is_signed_int(imm, 21, 1), "UJ out of range {:#x}", imm);
+ let imm = imm as u32;
+
+ // 0-6: opcode
+ let mut i = 0x3;
+ i |= opcode5 << 2;
+ i |= rd << 7;
+
+ // The displacement is completely hashed up.
+ i |= imm & 0xff000;
+ i |= ((imm >> 11) & 0x1) << 20;
+ i |= ((imm >> 1) & 0x3ff) << 21;
+ i |= ((imm >> 20) & 0x1) << 31;
+
+ sink.put4(i);
+}
diff --git a/third_party/rust/cranelift-codegen/src/isa/riscv/enc_tables.rs b/third_party/rust/cranelift-codegen/src/isa/riscv/enc_tables.rs
new file mode 100644
index 0000000000..76184ad727
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/isa/riscv/enc_tables.rs
@@ -0,0 +1,18 @@
+//! Encoding tables for RISC-V.
+
+use super::registers::*;
+use crate::ir;
+use crate::isa;
+use crate::isa::constraints::*;
+use crate::isa::enc_tables::*;
+use crate::isa::encoding::{base_size, RecipeSizing};
+use crate::predicates;
+
+// Include the generated encoding tables:
+// - `LEVEL1_RV32`
+// - `LEVEL1_RV64`
+// - `LEVEL2`
+// - `ENCLIST`
+// - `INFO`
+include!(concat!(env!("OUT_DIR"), "/encoding-riscv.rs"));
+include!(concat!(env!("OUT_DIR"), "/legalize-riscv.rs"));
diff --git a/third_party/rust/cranelift-codegen/src/isa/riscv/mod.rs b/third_party/rust/cranelift-codegen/src/isa/riscv/mod.rs
new file mode 100644
index 0000000000..e69a3a0e12
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/isa/riscv/mod.rs
@@ -0,0 +1,295 @@
+//! RISC-V Instruction Set Architecture.
+
+mod abi;
+mod binemit;
+mod enc_tables;
+mod registers;
+pub mod settings;
+
+use super::super::settings as shared_settings;
+#[cfg(feature = "testing_hooks")]
+use crate::binemit::CodeSink;
+use crate::binemit::{emit_function, MemoryCodeSink};
+use crate::ir;
+use crate::isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings};
+use crate::isa::Builder as IsaBuilder;
+use crate::isa::{EncInfo, RegClass, RegInfo, TargetIsa};
+use crate::regalloc;
+use alloc::borrow::Cow;
+use alloc::boxed::Box;
+use core::any::Any;
+use core::fmt;
+use target_lexicon::{PointerWidth, Triple};
+
+#[allow(dead_code)]
+struct Isa {
+ triple: Triple,
+ shared_flags: shared_settings::Flags,
+ isa_flags: settings::Flags,
+ cpumode: &'static [shared_enc_tables::Level1Entry<u16>],
+}
+
+/// Get an ISA builder for creating RISC-V targets.
+pub fn isa_builder(triple: Triple) -> IsaBuilder {
+ IsaBuilder {
+ triple,
+ setup: settings::builder(),
+ constructor: isa_constructor,
+ }
+}
+
+fn isa_constructor(
+ triple: Triple,
+ shared_flags: shared_settings::Flags,
+ builder: shared_settings::Builder,
+) -> Box<dyn TargetIsa> {
+ let level1 = match triple.pointer_width().unwrap() {
+ PointerWidth::U16 => panic!("16-bit RISC-V unrecognized"),
+ PointerWidth::U32 => &enc_tables::LEVEL1_RV32[..],
+ PointerWidth::U64 => &enc_tables::LEVEL1_RV64[..],
+ };
+ Box::new(Isa {
+ triple,
+ isa_flags: settings::Flags::new(&shared_flags, builder),
+ shared_flags,
+ cpumode: level1,
+ })
+}
+
+impl TargetIsa for Isa {
+ fn name(&self) -> &'static str {
+ "riscv"
+ }
+
+ fn triple(&self) -> &Triple {
+ &self.triple
+ }
+
+ fn flags(&self) -> &shared_settings::Flags {
+ &self.shared_flags
+ }
+
+ fn register_info(&self) -> RegInfo {
+ registers::INFO.clone()
+ }
+
+ fn encoding_info(&self) -> EncInfo {
+ enc_tables::INFO.clone()
+ }
+
+ fn legal_encodings<'a>(
+ &'a self,
+ func: &'a ir::Function,
+ inst: &'a ir::InstructionData,
+ ctrl_typevar: ir::Type,
+ ) -> Encodings<'a> {
+ lookup_enclist(
+ ctrl_typevar,
+ inst,
+ func,
+ self.cpumode,
+ &enc_tables::LEVEL2[..],
+ &enc_tables::ENCLISTS[..],
+ &enc_tables::LEGALIZE_ACTIONS[..],
+ &enc_tables::RECIPE_PREDICATES[..],
+ &enc_tables::INST_PREDICATES[..],
+ self.isa_flags.predicate_view(),
+ )
+ }
+
+ fn legalize_signature(&self, sig: &mut Cow<ir::Signature>, current: bool) {
+ abi::legalize_signature(sig, &self.triple, &self.isa_flags, current)
+ }
+
+ fn regclass_for_abi_type(&self, ty: ir::Type) -> RegClass {
+ abi::regclass_for_abi_type(ty)
+ }
+
+ fn allocatable_registers(&self, func: &ir::Function) -> regalloc::RegisterSet {
+ abi::allocatable_registers(func, &self.isa_flags)
+ }
+
+ #[cfg(feature = "testing_hooks")]
+ fn emit_inst(
+ &self,
+ func: &ir::Function,
+ inst: ir::Inst,
+ divert: &mut regalloc::RegDiversions,
+ sink: &mut dyn CodeSink,
+ ) {
+ binemit::emit_inst(func, inst, divert, sink, self)
+ }
+
+ fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
+ emit_function(func, binemit::emit_inst, sink, self)
+ }
+
+ fn unsigned_add_overflow_condition(&self) -> ir::condcodes::IntCC {
+ unimplemented!()
+ }
+
+ fn unsigned_sub_overflow_condition(&self) -> ir::condcodes::IntCC {
+ unimplemented!()
+ }
+
+ fn as_any(&self) -> &dyn Any {
+ self as &dyn Any
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::ir::{immediates, types};
+ use crate::ir::{Function, InstructionData, Opcode};
+ use crate::isa;
+ use crate::settings::{self, Configurable};
+ use alloc::string::{String, ToString};
+ use core::str::FromStr;
+ use target_lexicon::triple;
+
+ fn encstr(isa: &dyn isa::TargetIsa, enc: Result<isa::Encoding, isa::Legalize>) -> String {
+ match enc {
+ Ok(e) => isa.encoding_info().display(e).to_string(),
+ Err(_) => "no encoding".to_string(),
+ }
+ }
+
+ #[test]
+ fn test_64bitenc() {
+ let shared_builder = settings::builder();
+ let shared_flags = settings::Flags::new(shared_builder);
+ let isa = isa::lookup(triple!("riscv64"))
+ .unwrap()
+ .finish(shared_flags);
+
+ let mut func = Function::new();
+ let block = func.dfg.make_block();
+ let arg64 = func.dfg.append_block_param(block, types::I64);
+ let arg32 = func.dfg.append_block_param(block, types::I32);
+
+ // Try to encode iadd_imm.i64 v1, -10.
+ let inst64 = InstructionData::BinaryImm64 {
+ opcode: Opcode::IaddImm,
+ arg: arg64,
+ imm: immediates::Imm64::new(-10),
+ };
+
+ // ADDI is I/0b00100
+ assert_eq!(
+ encstr(&*isa, isa.encode(&func, &inst64, types::I64)),
+ "Ii#04"
+ );
+
+ // Try to encode iadd_imm.i64 v1, -10000.
+ let inst64_large = InstructionData::BinaryImm64 {
+ opcode: Opcode::IaddImm,
+ arg: arg64,
+ imm: immediates::Imm64::new(-10000),
+ };
+
+ // Immediate is out of range for ADDI.
+ assert!(isa.encode(&func, &inst64_large, types::I64).is_err());
+
+ // Create an iadd_imm.i32 which is encodable in RV64.
+ let inst32 = InstructionData::BinaryImm64 {
+ opcode: Opcode::IaddImm,
+ arg: arg32,
+ imm: immediates::Imm64::new(10),
+ };
+
+ // ADDIW is I/0b00110
+ assert_eq!(
+ encstr(&*isa, isa.encode(&func, &inst32, types::I32)),
+ "Ii#06"
+ );
+ }
+
+ // Same as above, but for RV32.
+ #[test]
+ fn test_32bitenc() {
+ let shared_builder = settings::builder();
+ let shared_flags = settings::Flags::new(shared_builder);
+ let isa = isa::lookup(triple!("riscv32"))
+ .unwrap()
+ .finish(shared_flags);
+
+ let mut func = Function::new();
+ let block = func.dfg.make_block();
+ let arg64 = func.dfg.append_block_param(block, types::I64);
+ let arg32 = func.dfg.append_block_param(block, types::I32);
+
+ // Try to encode iadd_imm.i64 v1, -10.
+ let inst64 = InstructionData::BinaryImm64 {
+ opcode: Opcode::IaddImm,
+ arg: arg64,
+ imm: immediates::Imm64::new(-10),
+ };
+
+ // In 32-bit mode, an i64 bit add should be narrowed.
+ assert!(isa.encode(&func, &inst64, types::I64).is_err());
+
+ // Try to encode iadd_imm.i64 v1, -10000.
+ let inst64_large = InstructionData::BinaryImm64 {
+ opcode: Opcode::IaddImm,
+ arg: arg64,
+ imm: immediates::Imm64::new(-10000),
+ };
+
+ // In 32-bit mode, an i64 bit add should be narrowed.
+ assert!(isa.encode(&func, &inst64_large, types::I64).is_err());
+
+ // Create an iadd_imm.i32 which is encodable in RV32.
+ let inst32 = InstructionData::BinaryImm64 {
+ opcode: Opcode::IaddImm,
+ arg: arg32,
+ imm: immediates::Imm64::new(10),
+ };
+
+ // ADDI is I/0b00100
+ assert_eq!(
+ encstr(&*isa, isa.encode(&func, &inst32, types::I32)),
+ "Ii#04"
+ );
+
+ // Create an imul.i32 which is encodable in RV32, but only when use_m is true.
+ let mul32 = InstructionData::Binary {
+ opcode: Opcode::Imul,
+ args: [arg32, arg32],
+ };
+
+ assert!(isa.encode(&func, &mul32, types::I32).is_err());
+ }
+
+ #[test]
+ fn test_rv32m() {
+ let shared_builder = settings::builder();
+ let shared_flags = settings::Flags::new(shared_builder);
+
+ // Set the supports_m stting which in turn enables the use_m predicate that unlocks
+ // encodings for imul.
+ let mut isa_builder = isa::lookup(triple!("riscv32")).unwrap();
+ isa_builder.enable("supports_m").unwrap();
+
+ let isa = isa_builder.finish(shared_flags);
+
+ let mut func = Function::new();
+ let block = func.dfg.make_block();
+ let arg32 = func.dfg.append_block_param(block, types::I32);
+
+ // Create an imul.i32 which is encodable in RV32M.
+ let mul32 = InstructionData::Binary {
+ opcode: Opcode::Imul,
+ args: [arg32, arg32],
+ };
+ assert_eq!(
+ encstr(&*isa, isa.encode(&func, &mul32, types::I32)),
+ "R#10c"
+ );
+ }
+}
+
+impl fmt::Display for Isa {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}\n{}", self.shared_flags, self.isa_flags)
+ }
+}
diff --git a/third_party/rust/cranelift-codegen/src/isa/riscv/registers.rs b/third_party/rust/cranelift-codegen/src/isa/riscv/registers.rs
new file mode 100644
index 0000000000..9043b7f65f
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/isa/riscv/registers.rs
@@ -0,0 +1,50 @@
+//! RISC-V register descriptions.
+
+use crate::isa::registers::{RegBank, RegClass, RegClassData, RegInfo, RegUnit};
+
+include!(concat!(env!("OUT_DIR"), "/registers-riscv.rs"));
+
+#[cfg(test)]
+mod tests {
+ use super::{FPR, GPR, INFO};
+ use crate::isa::RegUnit;
+ use alloc::string::{String, ToString};
+
+ #[test]
+ fn unit_encodings() {
+ assert_eq!(INFO.parse_regunit("x0"), Some(0));
+ assert_eq!(INFO.parse_regunit("x31"), Some(31));
+ assert_eq!(INFO.parse_regunit("f0"), Some(32));
+ assert_eq!(INFO.parse_regunit("f31"), Some(63));
+
+ assert_eq!(INFO.parse_regunit("x32"), None);
+ assert_eq!(INFO.parse_regunit("f32"), None);
+ }
+
+ #[test]
+ fn unit_names() {
+ fn uname(ru: RegUnit) -> String {
+ INFO.display_regunit(ru).to_string()
+ }
+
+ assert_eq!(uname(0), "%x0");
+ assert_eq!(uname(1), "%x1");
+ assert_eq!(uname(31), "%x31");
+ assert_eq!(uname(32), "%f0");
+ assert_eq!(uname(33), "%f1");
+ assert_eq!(uname(63), "%f31");
+ assert_eq!(uname(64), "%INVALID64");
+ }
+
+ #[test]
+ fn classes() {
+ assert!(GPR.contains(GPR.unit(0)));
+ assert!(GPR.contains(GPR.unit(31)));
+ assert!(!FPR.contains(GPR.unit(0)));
+ assert!(!FPR.contains(GPR.unit(31)));
+ assert!(!GPR.contains(FPR.unit(0)));
+ assert!(!GPR.contains(FPR.unit(31)));
+ assert!(FPR.contains(FPR.unit(0)));
+ assert!(FPR.contains(FPR.unit(31)));
+ }
+}
diff --git a/third_party/rust/cranelift-codegen/src/isa/riscv/settings.rs b/third_party/rust/cranelift-codegen/src/isa/riscv/settings.rs
new file mode 100644
index 0000000000..40aa3bed2b
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/isa/riscv/settings.rs
@@ -0,0 +1,56 @@
+//! RISC-V Settings.
+
+use crate::settings::{self, detail, Builder};
+use core::fmt;
+
+// Include code generated by `cranelift-codegen/meta/src/gen_settings.rs`. This file contains a
+// public `Flags` struct with an impl for all of the settings defined in
+// `cranelift-codegen/meta/src/isa/riscv/mod.rs`.
+include!(concat!(env!("OUT_DIR"), "/settings-riscv.rs"));
+
+#[cfg(test)]
+mod tests {
+ use super::{builder, Flags};
+ use crate::settings::{self, Configurable};
+ use alloc::string::ToString;
+
+ #[test]
+ fn display_default() {
+ let shared = settings::Flags::new(settings::builder());
+ let b = builder();
+ let f = Flags::new(&shared, b);
+ assert_eq!(
+ f.to_string(),
+ "[riscv]\n\
+ supports_m = false\n\
+ supports_a = false\n\
+ supports_f = false\n\
+ supports_d = false\n\
+ enable_m = true\n\
+ enable_e = false\n"
+ );
+ // Predicates are not part of the Display output.
+ assert_eq!(f.full_float(), false);
+ }
+
+ #[test]
+ fn predicates() {
+ let mut sb = settings::builder();
+ sb.set("enable_simd", "true").unwrap();
+ let shared = settings::Flags::new(sb);
+ let mut b = builder();
+ b.enable("supports_f").unwrap();
+ b.enable("supports_d").unwrap();
+ let f = Flags::new(&shared, b);
+ assert_eq!(f.full_float(), true);
+
+ let mut sb = settings::builder();
+ sb.set("enable_simd", "false").unwrap();
+ let shared = settings::Flags::new(sb);
+ let mut b = builder();
+ b.enable("supports_f").unwrap();
+ b.enable("supports_d").unwrap();
+ let f = Flags::new(&shared, b);
+ assert_eq!(f.full_float(), false);
+ }
+}