diff --git a/src/cpu.rs b/src/cpu.rs index 759dc40..ea4a16a 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -159,10 +159,35 @@ impl FRegisters { self.fregs[index as usize] } + /// read a single precision float a register, + /// expecting nanboxing according to the spec. + /// 21.2. NaN Boxing of Narrower Values + /// "[if] n f32 { + let bits = self.read(index).to_bits(); + if !bits <= u32::MAX as u64 { + f32::from_bits(bits as u32) + } else { + f32::NAN + } + } + /// Write the value to a register. pub fn write(&mut self, index: u64, value: f64) { self.fregs[index as usize] = value; } + + /// Write the single precision number to a register. + /// nan boxing them according to the spec + /// 21.2. NaN Boxing of Narrower Values + /// "Any operation that writes a narrower result to an 'f' register must write all 1s to the uppermost + /// FLEN-n bits to yield a legal NaN-boxedvalue." + pub fn write_nanboxed(&mut self, index: u64, value: f32) { + const NAN_MASK: u64 = !(u32::MAX as u64); + self.write(index, f64::from_bits(NAN_MASK | value.to_bits() as u64)); + } } impl fmt::Display for FRegisters { @@ -1790,7 +1815,6 @@ impl Cpu { self.write(addr, self.xregs.read(rs2), WORD)?; self.xregs.write(rd, 0); } else { - self.reservation_set.retain(|&x| x != addr); self.xregs.write(rd, 1); }; } @@ -1811,7 +1835,6 @@ impl Cpu { self.write(addr, self.xregs.read(rs2), DOUBLEWORD)?; self.xregs.write(rd, 0); } else { - self.reservation_set.retain(|&x| x != addr); self.xregs.write(rd, 1); } } @@ -2477,11 +2500,12 @@ impl Cpu { inst_count!(self, "fmadd.s"); self.debug(inst, "fmadd.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - (self.fregs.read(rs1) as f32) - .mul_add(self.fregs.read(rs2) as f32, self.fregs.read(rs3) as f32) - as f64, + (self.fregs.read_nanboxed(rs1)).mul_add( + self.fregs.read_nanboxed(rs2) , + self.fregs.read_nanboxed(rs3) , + ), ); } 0x1 => { @@ -2512,11 +2536,12 @@ impl Cpu { inst_count!(self, "fmsub.s"); self.debug(inst, "fmsub.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - (self.fregs.read(rs1) as f32) - .mul_add(self.fregs.read(rs2) as f32, -self.fregs.read(rs3) as f32) - as f64, + self.fregs.read_nanboxed(rs1).mul_add( + self.fregs.read_nanboxed(rs2), + -self.fregs.read_nanboxed(rs3), + ), ); } 0x1 => { @@ -2547,11 +2572,12 @@ impl Cpu { inst_count!(self, "fnmadd.s"); self.debug(inst, "fnmadd.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - (-self.fregs.read(rs1) as f32) - .mul_add(self.fregs.read(rs2) as f32, self.fregs.read(rs3) as f32) - as f64, + (-self.fregs.read_nanboxed(rs1)).mul_add( + self.fregs.read_nanboxed(rs2), + self.fregs.read_nanboxed(rs3), + ), ); } 0x1 => { @@ -2581,11 +2607,12 @@ impl Cpu { inst_count!(self, "fnmsub.s"); self.debug(inst, "fnmsub.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - (-self.fregs.read(rs1) as f32) - .mul_add(self.fregs.read(rs2) as f32, -self.fregs.read(rs3) as f32) - as f64, + (-self.fregs.read_nanboxed(rs1)).mul_add( + self.fregs.read_nanboxed(rs2), + -self.fregs.read_nanboxed(rs3), + ), ); } 0x1 => { @@ -2607,7 +2634,6 @@ impl Cpu { 0x53 => { // RV32F and RV64F // TODO: support the rounding mode encoding (rm). - // TODO: NaN Boxing of Narrower Values (Spec 12.2). // TODO: set exception flags. /* @@ -2639,9 +2665,9 @@ impl Cpu { inst_count!(self, "fadd.s"); self.debug(inst, "fadd.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - (self.fregs.read(rs1) as f32 + self.fregs.read(rs2) as f32) as f64, + self.fregs.read_nanboxed(rs1) + self.fregs.read_nanboxed(rs2), ) } 0x01 => { @@ -2657,9 +2683,9 @@ impl Cpu { inst_count!(self, "fsub.s"); self.debug(inst, "fsub.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - (self.fregs.read(rs1) as f32 - self.fregs.read(rs2) as f32) as f64, + self.fregs.read_nanboxed(rs1) - self.fregs.read_nanboxed(rs2), ) } 0x05 => { @@ -2675,9 +2701,9 @@ impl Cpu { inst_count!(self, "fmul.s"); self.debug(inst, "fmul.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - (self.fregs.read(rs1) as f32 * self.fregs.read(rs2) as f32) as f64, + self.fregs.read_nanboxed(rs1) * self.fregs.read_nanboxed(rs2), ) } 0x09 => { @@ -2693,9 +2719,9 @@ impl Cpu { inst_count!(self, "fdiv.s"); self.debug(inst, "fdiv.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - (self.fregs.read(rs1) as f32 / self.fregs.read(rs2) as f32) as f64, + self.fregs.read_nanboxed(rs1) / self.fregs.read_nanboxed(rs2), ) } 0x0d => { @@ -2713,17 +2739,23 @@ impl Cpu { inst_count!(self, "fsgnj.s"); self.debug(inst, "fsgnj.s"); - self.fregs - .write(rd, self.fregs.read(rs1).copysign(self.fregs.read(rs2))); + self.fregs.write_nanboxed( + rd, + self.fregs + .read_nanboxed(rs1) + .copysign(self.fregs.read_nanboxed(rs2)), + ); } 0x1 => { // fsgnjn.s inst_count!(self, "fsgnjn.s"); self.debug(inst, "fsgnjn.s"); - self.fregs.write( + self.fregs.write_nanboxed( rd, - self.fregs.read(rs1).copysign(-self.fregs.read(rs2)), + self.fregs + .read_nanboxed(rs1) + .copysign(-self.fregs.read_nanboxed(rs2)), ); } 0x2 => { @@ -2731,11 +2763,11 @@ impl Cpu { inst_count!(self, "fsgnjx.s"); self.debug(inst, "fsgnjx.s"); - let sign1 = (self.fregs.read(rs1) as f32).to_bits() & 0x80000000; - let sign2 = (self.fregs.read(rs2) as f32).to_bits() & 0x80000000; - let other = (self.fregs.read(rs1) as f32).to_bits() & 0x7fffffff; + let sign1 = self.fregs.read_nanboxed(rs1).to_bits() & 0x80000000; + let sign2 = self.fregs.read_nanboxed(rs2).to_bits() & 0x80000000; + let other = self.fregs.read_nanboxed(rs1).to_bits() & 0x7fffffff; self.fregs - .write(rd, f32::from_bits((sign1 ^ sign2) | other) as f64); + .write_nanboxed(rd, f32::from_bits((sign1 ^ sign2) | other)); } _ => { return Err(Exception::IllegalInstruction(inst)); @@ -2785,16 +2817,24 @@ impl Cpu { inst_count!(self, "fmin.s"); self.debug(inst, "fmin.s"); - self.fregs - .write(rd, self.fregs.read(rs1).min(self.fregs.read(rs2))); + self.fregs.write_nanboxed( + rd, + self.fregs + .read_nanboxed(rs1) + .min(self.fregs.read_nanboxed(rs2)), + ); } 0x1 => { // fmax.s inst_count!(self, "fmax.s"); self.debug(inst, "fmax.s"); - self.fregs - .write(rd, self.fregs.read(rs1).max(self.fregs.read(rs2))); + self.fregs.write_nanboxed( + rd, + self.fregs + .read_nanboxed(rs1) + .max(self.fregs.read_nanboxed(rs2)), + ); } _ => { return Err(Exception::IllegalInstruction(inst)); @@ -2829,14 +2869,14 @@ impl Cpu { inst_count!(self, "fcvt.s.d"); self.debug(inst, "fcvt.s.d"); - self.fregs.write(rd, self.fregs.read(rs1)); + self.fregs.write_nanboxed(rd, self.fregs.read(rs1) as f32); } 0x21 => { // fcvt.d.s inst_count!(self, "fcvt.d.s"); self.debug(inst, "fcvt.d.s"); - self.fregs.write(rd, (self.fregs.read(rs1) as f32) as f64); + self.fregs.write(rd, self.fregs.read_nanboxed(rs1) as f64); } 0x2c => { // fsqrt.s @@ -2844,7 +2884,7 @@ impl Cpu { self.debug(inst, "fsqrt.s"); self.fregs - .write(rd, (self.fregs.read(rs1) as f32).sqrt() as f64); + .write_nanboxed(rd, self.fregs.read_nanboxed(rs1).sqrt()); } 0x2d => { // fsqrt.d @@ -2862,7 +2902,9 @@ impl Cpu { self.xregs.write( rd, - if self.fregs.read(rs1) <= self.fregs.read(rs2) { + if self.fregs.read_nanboxed(rs1) + <= self.fregs.read_nanboxed(rs2) + { 1 } else { 0 @@ -2876,7 +2918,8 @@ impl Cpu { self.xregs.write( rd, - if self.fregs.read(rs1) < self.fregs.read(rs2) { + if self.fregs.read_nanboxed(rs1) < self.fregs.read_nanboxed(rs2) + { 1 } else { 0 @@ -2890,7 +2933,9 @@ impl Cpu { self.xregs.write( rd, - if self.fregs.read(rs1) == self.fregs.read(rs2) { + if self.fregs.read_nanboxed(rs1) + == self.fregs.read_nanboxed(rs2) + { 1 } else { 0 @@ -2960,7 +3005,7 @@ impl Cpu { self.xregs.write( rd, - ((self.fregs.read(rs1) as f32).round() as i32) as u64, + (self.fregs.read_nanboxed(rs1).round() as i32) as u64, ); } 0x1 => { @@ -2970,7 +3015,7 @@ impl Cpu { self.xregs.write( rd, - (((self.fregs.read(rs1) as f32).round() as u32) as i32) as u64, + ((self.fregs.read_nanboxed(rs1).round() as u32) as i32) as u64, ); } 0x2 => { @@ -2979,7 +3024,7 @@ impl Cpu { self.debug(inst, "fcvt.l.s"); self.xregs - .write(rd, (self.fregs.read(rs1) as f32).round() as u64); + .write(rd, self.fregs.read_nanboxed(rs1).round() as u64); } 0x3 => { // fcvt.lu.s @@ -2987,7 +3032,7 @@ impl Cpu { self.debug(inst, "fcvt.lu.s"); self.xregs - .write(rd, (self.fregs.read(rs1) as f32).round() as u64); + .write(rd, self.fregs.read_nanboxed(rs1).round() as u64); } _ => { return Err(Exception::IllegalInstruction(inst)); @@ -3041,7 +3086,7 @@ impl Cpu { self.debug(inst, "fcvt.s.w"); self.fregs - .write(rd, ((self.xregs.read(rs1) as i32) as f32) as f64); + .write_nanboxed(rd, (self.xregs.read(rs1) as i32) as f32); } 0x1 => { // fcvt.s.wu @@ -3049,14 +3094,15 @@ impl Cpu { self.debug(inst, "fcvt.s.wu"); self.fregs - .write(rd, ((self.xregs.read(rs1) as u32) as f32) as f64); + .write_nanboxed(rd, (self.xregs.read(rs1) as u32) as f32); } 0x2 => { // fcvt.s.l inst_count!(self, "fcvt.s.l"); self.debug(inst, "fcvt.s.l"); - self.fregs.write(rd, (self.xregs.read(rs1) as f32) as f64); + self.fregs + .write_nanboxed(rd, self.xregs.read(rs1) as i64 as f32); } 0x3 => { // fcvt.s.lu @@ -3064,7 +3110,7 @@ impl Cpu { self.debug(inst, "fcvt.s.lu"); self.fregs - .write(rd, ((self.xregs.read(rs1) as u64) as f32) as f64); + .write_nanboxed(rd, (self.xregs.read(rs1) as u64) as f32); } _ => { return Err(Exception::IllegalInstruction(inst)); @@ -3124,7 +3170,7 @@ impl Cpu { inst_count!(self, "fclass.s"); self.debug(inst, "fclass.s"); - let f = self.fregs.read(rs1); + let f = self.fregs.read_nanboxed(rs1); match f.classify() { FpCategory::Infinite => { self.xregs diff --git a/tests/helper.rs b/tests/helper.rs index e0ea3d9..21644cf 100644 --- a/tests/helper.rs +++ b/tests/helper.rs @@ -59,3 +59,8 @@ pub fn run( ); } } + +pub fn nan_box(num: f32) -> f64 { + let NAN_MASK = !(u32::MAX as u64); + f64::from_bits(NAN_MASK | (num.to_bits() as u64)) +} diff --git a/tests/rv32d.rs b/tests/rv32d.rs index e7bbc77..b5e7bd9 100644 --- a/tests/rv32d.rs +++ b/tests/rv32d.rs @@ -259,7 +259,7 @@ fn fcvtsd_rd_rs1_rs2() { 0xd3, 0x0f, 0x1f, 0x40, // fcvt.s.d f31, f30 ]; let expected_xregs = helper::create_xregs(vec![]); - let expected_fregs = helper::create_fregs(vec![(31, -1.2), (30, -1.2)]); + let expected_fregs = helper::create_fregs(vec![(31, helper::nan_box(-1.2)), (30, -1.2)]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } diff --git a/tests/rv32f.rs b/tests/rv32f.rs index 5205ba9..d0000a0 100644 --- a/tests/rv32f.rs +++ b/tests/rv32f.rs @@ -179,15 +179,18 @@ fn fdivs_rd_rs1_rs2() { fn fsgnjs_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(29, -1.2); - emu.cpu.fregs.write(30, 4.2); + emu.cpu.fregs.write_nanboxed(29, -1.2); + emu.cpu.fregs.write_nanboxed(30, 4.2); let data = vec![ 0xd3, 0x0f, 0xdf, 0x21, // fsgnj.s f31, f30, f29 ]; let expected_xregs = helper::create_xregs(vec![]); - let expected_fregs = helper::create_fregs(vec![(31, -4.2), (30, 4.2), (29, -1.2)]); - + let expected_fregs = helper::create_fregs(vec![ + (31, helper::nan_box(-4.2)), + (30, helper::nan_box(4.2)), + (29, helper::nan_box(-1.2)), + ]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -195,14 +198,18 @@ fn fsgnjs_rd_rs1_rs2() { fn fsgnjns_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(29, -1.2); - emu.cpu.fregs.write(30, 4.2); + emu.cpu.fregs.write_nanboxed(29, -1.2); + emu.cpu.fregs.write_nanboxed(30, 4.2); let data = vec![ 0xd3, 0x1f, 0xdf, 0x21, // fsgnjn.s f31, f30, f29 ]; let expected_xregs = helper::create_xregs(vec![]); - let expected_fregs = helper::create_fregs(vec![(31, 4.2), (30, 4.2), (29, -1.2)]); + let expected_fregs = helper::create_fregs(vec![ + (31, helper::nan_box(4.2)), + (30, helper::nan_box(4.2)), + (29, helper::nan_box(-1.2)), + ]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -228,14 +235,18 @@ fn fsgnjxs_rd_rs1_rs2() { fn fmins_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(29, 4.2); - emu.cpu.fregs.write(30, -1.2); + emu.cpu.fregs.write_nanboxed(29, 4.2); + emu.cpu.fregs.write_nanboxed(30, -1.2); let data = vec![ 0xd3, 0x0f, 0xdf, 0x29, // fmin.s f31, f30, f29 ]; let expected_xregs = helper::create_xregs(vec![]); - let expected_fregs = helper::create_fregs(vec![(31, -1.2), (30, -1.2), (29, 4.2)]); + let expected_fregs = helper::create_fregs(vec![ + (31, helper::nan_box(-1.2)), + (30, helper::nan_box(-1.2)), + (29, helper::nan_box(4.2)), + ]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -244,14 +255,18 @@ fn fmins_rd_rs1_rs2() { fn fmaxs_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(29, 4.2); - emu.cpu.fregs.write(30, -1.2); + emu.cpu.fregs.write_nanboxed(29, 4.2); + emu.cpu.fregs.write_nanboxed(30, -1.2); let data = vec![ 0xd3, 0x1f, 0xdf, 0x29, // fmax.s f31, f30, f29 ]; let expected_xregs = helper::create_xregs(vec![]); - let expected_fregs = helper::create_fregs(vec![(31, 4.2), (30, -1.2), (29, 4.2)]); + let expected_fregs = helper::create_fregs(vec![ + (31, helper::nan_box(4.2)), + (30, helper::nan_box(-1.2)), + (29, helper::nan_box(4.2)), + ]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -260,13 +275,16 @@ fn fmaxs_rd_rs1_rs2() { fn fsqrts_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(30, 4.2); + emu.cpu.fregs.write_nanboxed(30, 4.2); let data = vec![ 0xd3, 0x0f, 0x0f, 0x58, // fmax.s f31, f30 ]; let expected_xregs = helper::create_xregs(vec![]); - let expected_fregs = helper::create_fregs(vec![(31, 2.0493900775909424), (30, 4.2)]); + let expected_fregs = helper::create_fregs(vec![ + (31, helper::nan_box(2.0493900775909424)), + (30, helper::nan_box(4.2)), + ]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -275,14 +293,15 @@ fn fsqrts_rd_rs1_rs2() { fn fles_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(29, 4.2); - emu.cpu.fregs.write(30, 4.2); + emu.cpu.fregs.write_nanboxed(29, 4.2); + emu.cpu.fregs.write_nanboxed(30, 4.2); let data = vec![ 0xd3, 0x0f, 0xdf, 0xa1, // fle.s f31, f30, f29 ]; let expected_xregs = helper::create_xregs(vec![(31, 1)]); - let expected_fregs = helper::create_fregs(vec![(30, 4.2), (29, 4.2)]); + let expected_fregs = + helper::create_fregs(vec![(30, helper::nan_box(4.2)), (29, helper::nan_box(4.2))]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -291,14 +310,17 @@ fn fles_rd_rs1_rs2() { fn flts_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(29, 4.2); - emu.cpu.fregs.write(30, -1.2); + emu.cpu.fregs.write_nanboxed(29, 4.2); + emu.cpu.fregs.write_nanboxed(30, -1.2); let data = vec![ 0xd3, 0x1f, 0xdf, 0xa1, // flt.s f31, f30, f29 ]; let expected_xregs = helper::create_xregs(vec![(31, 1)]); - let expected_fregs = helper::create_fregs(vec![(30, -1.2), (29, 4.2)]); + let expected_fregs = helper::create_fregs(vec![ + (30, helper::nan_box(-1.2)), + (29, helper::nan_box(4.2)), + ]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -307,14 +329,15 @@ fn flts_rd_rs1_rs2() { fn feqs_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(29, 4.2); - emu.cpu.fregs.write(30, 4.2); + emu.cpu.fregs.write_nanboxed(29, 4.2); + emu.cpu.fregs.write_nanboxed(30, 4.2); let data = vec![ 0xd3, 0x2f, 0xdf, 0xa1, // feq.s f31, f30, f29 ]; let expected_xregs = helper::create_xregs(vec![(31, 1)]); - let expected_fregs = helper::create_fregs(vec![(30, 4.2), (29, 4.2)]); + let expected_fregs = + helper::create_fregs(vec![(30, helper::nan_box(4.2)), (29, helper::nan_box(4.2))]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -323,13 +346,13 @@ fn feqs_rd_rs1_rs2() { fn fcvtws_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(31, -4.2); + emu.cpu.fregs.write_nanboxed(31, -4.2); let data = vec![ 0xd3, 0x8f, 0x0f, 0xc0, // fcvt.w.s x31, f31 (rm: 000) ]; let expected_xregs = helper::create_xregs(vec![(31, -4 as i64 as u64)]); - let expected_fregs = helper::create_fregs(vec![(31, -4.2)]); + let expected_fregs = helper::create_fregs(vec![(31, helper::nan_box(-4.2))]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -338,13 +361,13 @@ fn fcvtws_rd_rs1_rs2() { fn fcvtwus_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(31, 4.2); + emu.cpu.fregs.write_nanboxed(31, 4.2); let data = vec![ 0xd3, 0x8f, 0x1f, 0xc0, // fcvt.wu.s x31, f31 (rm: 000) ]; let expected_xregs = helper::create_xregs(vec![(31, 4)]); - let expected_fregs = helper::create_fregs(vec![(31, 4.2)]); + let expected_fregs = helper::create_fregs(vec![(31, helper::nan_box(4.2))]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -359,7 +382,7 @@ fn fcvtsw_rd_rs1_rs2() { 0xd3, 0x8f, 0x0f, 0xd0, // fcvt.s.w x31, f31 (rm: 000) ]; let expected_xregs = helper::create_xregs(vec![(31, -4 as i64 as u64)]); - let expected_fregs = helper::create_fregs(vec![(31, -4.0)]); + let expected_fregs = helper::create_fregs(vec![(31, helper::nan_box(-4.0))]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -374,7 +397,7 @@ fn fcvtswu_rd_rs1_rs2() { 0xd3, 0x8f, 0x1f, 0xd0, // fcvt.s.wu x31, f31 (rm: 000) ]; let expected_xregs = helper::create_xregs(vec![(31, 4)]); - let expected_fregs = helper::create_fregs(vec![(31, 4.0)]); + let expected_fregs = helper::create_fregs(vec![(31, helper::nan_box(4.0))]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); } @@ -398,13 +421,13 @@ fn fmvxw_rd_rs1_rs2() { fn fclasss_rd_rs1_rs2() { let mut emu = Emulator::new(); - emu.cpu.fregs.write(31, std::f64::INFINITY); + emu.cpu.fregs.write_nanboxed(31, std::f32::INFINITY); let data = vec![ 0xd3, 0x9f, 0x0f, 0xe0, // fclass.s x31, f31 ]; let expected_xregs = helper::create_xregs(vec![(31, 7)]); - let expected_fregs = helper::create_fregs(vec![(31, f64::INFINITY)]); + let expected_fregs = helper::create_fregs(vec![(31, helper::nan_box(std::f32::INFINITY))]); helper::run(&mut emu, data, &expected_xregs, &expected_fregs); }