diff --git a/2024/04/p1/build.sh b/2024/04/p1/build.sh new file mode 100755 index 0000000..d71f740 --- /dev/null +++ b/2024/04/p1/build.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -euxo pipefail + +rustup run nightly rustc \ + --target x86_64-unknown-none \ + -Crelocation-model=static \ + \ + -Copt-level=3 \ + -Ctarget-cpu=native \ + -Cpanic=abort \ + -Cstrip=symbols \ + -Coverflow_checks=n \ + -Clto \ + \ + $* -o main main.rs diff --git a/2024/04/p1/main.rs b/2024/04/p1/main.rs index d32f84f..39f6903 100644 --- a/2024/04/p1/main.rs +++ b/2024/04/p1/main.rs @@ -1,30 +1,255 @@ -use std::{ - cmp, - fs::File, - io::Read, - time::{Duration, Instant}, -}; +#![no_std] +#![no_main] +#![feature(naked_functions)] +#![allow(static_mut_refs)] -fn main() -> std::io::Result<()> { - let args: Vec<_> = std::env::args().collect(); - let filename = &args[1]; - let runs: u32 = args[2] +use core::{ + arch::{asm, naked_asm}, + cmp, + ffi::CStr, + fmt::{self, Debug, Write}, + mem, ops, + panic::PanicInfo, + usize, +}; +// use std::{fs::File, io::Read}; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct CInstant { + secs: i64, // time_t + nanos: i64, // long +} + +impl CInstant { + fn now() -> Self { + let clockid: isize = 1; // CLOCK_MONOTONIC (non-jumping-backwards uptime) + let instant = unsafe { + let mut instant: Self = mem::zeroed(); + let mut result: isize; + + asm!( + "syscall", + in("rax") 228, + in("rsi") &mut instant, + in("rdi") clockid, + lateout("rax") result, + out("rcx") _, + out("r11") _, + options(nostack) + ); + + if result < 0 { + Err(result) + } else { + Ok(instant) + } + }; + + instant.expect("cant even trust syscalls on stack nowadays") + } + + pub fn as_nanos(self) -> i128 { + (self.secs as i128) * 1_000_000_000 + (self.nanos as i128) + } +} + +impl ops::Sub for CInstant { + type Output = CDuration; + fn sub(self, rhs: Self) -> Self::Output { + CDuration::from(self.as_nanos() - rhs.as_nanos()) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct CDuration { + pub nanos: i128, +} + +const IS_SUB_UNITS: &[char] = &['n', 'μ', 'm']; +impl Debug for CDuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut i = 0; + let max_idx = 3; + let mut amount = self.nanos; + + while amount >= 1000 && i < max_idx { + amount /= 1000; + i += 1; + } + + write!(f, "{amount}")?; + if i != 3 { + write!(f, "{}", IS_SUB_UNITS[i])?; + } + + write!(f, "s") + } +} + +impl CDuration { + pub const ZERO: CDuration = CDuration::from(0); + pub const MAX: CDuration = CDuration::from(i128::MAX); + + pub const fn from(nanos: i128) -> Self { + Self { nanos } + } +} + +impl ops::DivAssign for CDuration { + fn div_assign(&mut self, rhs: u32) { + self.nanos /= rhs as i128 + } +} + +impl ops::Div for CDuration { + type Output = CDuration; + fn div(mut self, rhs: u32) -> Self::Output { + self /= rhs; + self + } +} + +impl ops::AddAssign for CDuration { + fn add_assign(&mut self, rhs: Self) { + self.nanos += rhs.nanos; + } +} + +impl ops::Neg for CDuration { + type Output = Self; + fn neg(mut self) -> Self::Output { + self.nanos = -self.nanos; + self + } +} + +// FD +struct FDWriter(usize); + +impl FDWriter { + pub const fn from_fd(fd: usize) -> Self { + Self(fd) + } +} + +impl Write for FDWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + unsafe { + let result: isize; + asm!( + "syscall", + in("rax") 1, // write + in("rdi") self.0, // fd + in("rsi") s.as_ptr() as *const u8, // *data + in("rdx") s.as_bytes().len(), // data len + lateout("rax") result, + out("rcx") _, + out("r11") _, + options(nostack) + ); + + if result < 0 { + Err(fmt::Error) + } else { + Ok(()) + } + } + } +} + +#[naked] +#[no_mangle] +pub extern "C" fn _start() { + unsafe { + naked_asm!( + "mov {0}, rsp", + "jmp {1}", + sym STACK_START, + sym main + ); + } +} + +static mut ARGC: usize = 1; +static mut STACK_START: *const () = unsafe { mem::zeroed() }; +fn get_arg(idx: usize) -> Option<&'static CStr> { + unsafe { + if idx >= ARGC { + return None; + } + + let ptr = ((STACK_START as usize) + (idx * 8) + 8) as *const *const i8; + let cstr = core::ffi::CStr::from_ptr(*ptr); + Some(cstr) + } +} + +pub fn exit(code: isize) -> ! { + unsafe { + asm!( + "syscall", + in("rax") 60, + in("rdi") code, + options(nostack, noreturn) + ); + } +} + +static mut STDOUT: FDWriter = FDWriter::from_fd(1); +static mut STDERR: FDWriter = FDWriter::from_fd(2); +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + unsafe { + _ = writeln!(&mut STDERR, "\x1b[1;31m{info:?}\x1b[0m"); + } + exit(1); +} + +pub fn unsafe_sized_slice_collect>(slice: &mut [T], iter: I) -> usize { + let mut i = 0; + for e in iter { + slice[i] = e; + i += 1; + } + i +} + +pub extern "C" fn main() -> ! { + unsafe { + ARGC = *(STACK_START as *const usize); + } + + // let args: Vec<_> = std::env::args().collect(); + // let filename = &args[1]; + let runs: u32 = get_arg(1) + .expect("no arg 1") + .to_str() + .expect("invalid string encoding") .parse() .expect("did not receive a number as `runs` argument"); - let mut contents = vec![]; - let mut file = File::open(filename)?; - file.read_to_end(&mut contents)?; - let lines: Vec<_> = contents.split(|&b| b == b'\n').collect(); + // let mut contents = vec![]; + // let mut file = File::open(filename)?; + // file.read_to_end(&mut contents)?; + let contents: &[u8] = include_bytes!("../input.txt"); - let mut max = Duration::ZERO; - let mut avg = Duration::ZERO; - let mut min = Duration::MAX; - let result = run(&lines); + let mut lines: [&[u8]; 1024] = [&[]; 1024]; + let len = unsafe_sized_slice_collect(&mut lines, contents.split(|&b| b == b'\n')); + + let mut max = CDuration::ZERO; + let mut avg = CDuration::ZERO; + let mut min = CDuration::MAX; + _ = writeln!( + unsafe { &mut STDOUT }, + "\x1b[0;1;31;43m[W]\x1b[0;1;33m syscall time {:?}\x1b[0m", + -(CInstant::now() - CInstant::now()) + ); + let result = run(&lines, len); for i in 0..runs { - let a = Instant::now(); - let run_result = run(&lines); - let b = Instant::now(); + let a = CInstant::now(); + let run_result = run(&lines, len); + let b = CInstant::now(); if result != run_result { panic!( "supposed result is {} but this run got {} after {} runs", @@ -38,13 +263,15 @@ fn main() -> std::io::Result<()> { avg += d / runs; } - println!("\x1b[0;34mtotal \x1b[1;33m{result}\x1b[0;34m\n runs \x1b[1;33m{runs:?}\x1b[0;34m\n avg. \x1b[1;35m{avg:?}\x1b[0;34m\n max. \x1b[1;31m{max:?}\x1b[0;34m\n min. \x1b[1;32m{min:?}\x1b[0m"); - Ok(()) + _ = writeln!(unsafe { &mut STDOUT }, "\x1b[0;34mtotal \x1b[1;33m{result}\x1b[0;34m\n runs \x1b[1;33m{runs:?}\x1b[0;34m\n avg. \x1b[1;35m{avg:?}\x1b[0;34m\n max. \x1b[1;31m{max:?}\x1b[0;34m\n min. \x1b[1;32m{min:?}\x1b[0m"); + + exit(0); } -fn run(lines: &[&[u8]]) -> usize { +fn run(lines: &[&[u8]], len: usize) -> usize { let mut t = 0; - for (i, ln) in lines.iter().enumerate() { + for i in 0..len { + let ln = lines[i]; for (j, &ch) in ln.iter().enumerate() { if ch == b'X' { for dir in [