ddnet-client --map something.map
error_generic
and not_valid
doesn't say what the generic error is nor what is not valid on the form_test.go
and name the functions with prefix Test
, it even has builtin coverage, so you can just add -cover
and it'll calculate coverage too while testinghyperfine
t.Parallel()
error_generic
and not_valid
doesn't say what the generic error is nor what is not valid on the form ./DDNet "my map.map"
runs the console command "my" and passes "map.map" as argument.std::get
thoughA third precaution: The notation used in
this paper differs considerably from that
used by von Neumann, so that modern
readers can more easily understand what he
did. Where he would write, for instance,
15) c -}- (m' -- 1)(p ~- 1) ~-~ 14 [ p ~- 2,
we will use an equivalent assembly-like
language form,
MOVEIN PIK p~- 1, BUFFER, [YPTR].
_invoke_trampoline:
# rdi <- fn_ptr: extern "C" fn()
# rsi <- args_ptr: *const u64
# rdx <- args_len: usize
# rcx <- ret_ptr: &mut [u64; 2]
push rbp # Push rbp (callee-saved).
push rcx # Push rcx (ret_ptr).
mov rbp, rsp # Store the current stack pointer.
sub rsp, 8 # Align the stack.
mov r10, rdi # We'll need rdi.
mov r11, rsi # We'll need rsi.
cmp rdx, 6 # Check if there are more than 6 arguments.
jbe 2f # If there are less than 6, skip to register arguments.
#
# Process stack arguments.
#
# Add padding to support an odd number of stack parameters.
mov rax, rdx
and rax, 1
lea rsp, [rsp + 8 * rax]
1:
dec rdx # Decrement length.
mov rax, [r11 + 8 * rdx] # Load the value.
push rax # Push it into the stack.
cmp rdx, 6 # Check if there are more than 6 arguments.
ja 1b # If there still are, loop back and repeat.
2:
#
# Process registers.
#
shl rdx, 2 # Multiply remaining length by 4.
lea rax, [rip + 3f] # Load the PC-relative address of `3f`.
sub rax, rdx # Subtract 4 * remaining_len (rdx).
jmp rax # Jump to the resulting address.
mov r9, [r11 + 0x28] # Load argument #6.
mov r8, [r11 + 0x20] # Load argument #5.
mov rcx, [r11 + 0x18] # Load argument #4.
mov rdx, [r11 + 0x10] # Load argument #3.
mov rsi, [r11 + 0x08] # Load argument #2.
nop # Note: The previous 5 `mov` instructions use 4 bytes each, but
# the last one only takes 3. This `nop` (1 byte) is used to
# align them all at 4 bytes so that the last jump instruction
# works correctly.
mov rdi, [r11] # Load argument #1.
3:
# Call the function.
call r10
mov rsp, rbp
pop rcx
pop rbp
# Store return registers.
mov [rcx], rax
mov [rcx + 8], rdx
ret
this is like our trampoline method to call a function with X amount of arguments ./DDNet "my map.map"
runs the console command "my" and passes "map.map" as argument. DDNet "data/maps/Sunny Side Up.map"
pub fn push_aligned(&mut self, align: usize, mut values: &[u64]) {
assert!(align.is_power_of_two());
assert!(align <= 16);
#[cfg(target_arch = "x86_64")]
const NUM_REGISTER_ARGS: usize = 6;
#[cfg(target_arch = "aarch64")]
const NUM_REGISTER_ARGS: usize = 8;
if align == 16 {
// This works because on both aarch64 and x86_64 the stack is already aligned to
// 16 bytes when the trampoline starts pushing values.
// Whenever a value spans across multiple registers, if it's in a position where it would be split between
// registers and the stack it must be padded so that the entire value is stored within the stack.
if self.invoke_data.len() >= NUM_REGISTER_ARGS {
if self.invoke_data.len() & 1 != 0 {
self.invoke_data.push(0);
}
} else if self.invoke_data.len() + 1 >= NUM_REGISTER_ARGS {
self.invoke_data.push(0);
} else {
let new_len = self.invoke_data.len() + values.len();
if new_len >= NUM_REGISTER_ARGS && new_len % 2 != 0 {
let chunk;
(chunk, values) = if values.len() >= 4 {
values.split_at(4)
} else {
(values, [].as_slice())
};
self.invoke_data.extend(chunk);
self.invoke_data.push(0);
}
}
}
self.invoke_data.extend(values);
}
pub fn push_aligned(&mut self, align: usize, mut values: &[u64]) {
assert!(align.is_power_of_two());
assert!(align <= 16);
#[cfg(target_arch = "x86_64")]
const NUM_REGISTER_ARGS: usize = 6;
#[cfg(target_arch = "aarch64")]
const NUM_REGISTER_ARGS: usize = 8;
if align == 16 {
// This works because on both aarch64 and x86_64 the stack is already aligned to
// 16 bytes when the trampoline starts pushing values.
// Whenever a value spans across multiple registers, if it's in a position where it would be split between
// registers and the stack it must be padded so that the entire value is stored within the stack.
if self.invoke_data.len() >= NUM_REGISTER_ARGS {
if self.invoke_data.len() & 1 != 0 {
self.invoke_data.push(0);
}
} else if self.invoke_data.len() + 1 >= NUM_REGISTER_ARGS {
self.invoke_data.push(0);
} else {
let new_len = self.invoke_data.len() + values.len();
if new_len >= NUM_REGISTER_ARGS && new_len % 2 != 0 {
let chunk;
(chunk, values) = if values.len() >= 4 {
values.split_at(4)
} else {
(values, [].as_slice())
};
self.invoke_data.extend(chunk);
self.invoke_data.push(0);
}
}
}
self.invoke_data.extend(values);
}
./DDNet "my map.map"
runs the console command "my" and passes "map.map" as argument. __builtin_apply
but it's just too weak