diff options
Diffstat (limited to 'third_party/rust/cranelift-codegen/src/isa/riscv')
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); + } +} |