use crate::{
compiler::{
code_space::CodeBuffer,
graph::{Signature, Type},
},
rt::Value,
};
use gimli::write::CallFrameInstruction;
use gimli::write::FrameDescriptionEntry;
use gimli::write::{Address, EhFrame, EndianVec, FrameTable};
use gimli::LittleEndian;
use gimli::{write::CommonInformationEntry, Encoding, Format, X86_64};
extern "C" {
fn __register_frame(v: usize);
}
fn create_cie() -> CommonInformationEntry {
let mut entry = CommonInformationEntry::new(
Encoding {
address_size: 8,
format: Format::Dwarf32,
version: 1,
},
1,
-8,
X86_64::RA,
);
entry.add_instruction(CallFrameInstruction::Cfa(X86_64::RSP, 8));
entry.add_instruction(CallFrameInstruction::Offset(X86_64::RA, -8));
entry
}
#[allow(unused)]
pub fn register_unwind_info(buf: &CodeBuffer) {
let cie = create_cie();
let mut dwarf_frametable = FrameTable::default();
let cie_id = dwarf_frametable.add_cie(cie);
let up_to_sp = 16;
use CallFrameInstruction::*;
let unwind_instructions = vec![
(1, CfaOffset(up_to_sp as i32)),
(1, Offset(X86_64::RBP, -(up_to_sp as i32))),
(4, CfaRegister(X86_64::RBP)),
];
let fde = {
let mut fde =
FrameDescriptionEntry::new(Address::Constant(buf.as_ptr() as _), buf.len() as _);
for (offset, inst) in &unwind_instructions {
fde.add_instruction(*offset, inst.clone());
}
fde
};
dwarf_frametable.add_fde(cie_id, fde);
let mut eh_frame = Box::leak(Box::new(EhFrame(EndianVec::new(LittleEndian))));
dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
let ptr = eh_frame.slice().as_ptr();
unsafe { __register_frame(ptr as _) }
}
pub fn invoke_compiled_method(
signature: &Signature,
entry: *const u8,
args: &[Value],
) -> Option<Value> {
let mut raw_args = vec![];
for arg in args {
let raw = match *arg {
Value::Bool(x) => RawValue(0, x as _),
Value::I8(x) => RawValue(1, x as _),
Value::I16(x) => RawValue(2, x as _),
Value::I32(x) => RawValue(3, x as _),
Value::I64(x) => RawValue(4, x as _),
Value::F32(x) => RawValue(5, {
let v = (x, 0i32);
unsafe { std::mem::transmute::<_, u64>(v) }
}),
Value::F64(x) => RawValue(6, unsafe { std::mem::transmute::<_, u64>(x) }),
};
raw_args.push(raw);
}
let mut result: u64 = 0;
let result_tag = match signature.1 {
Type::Bool | Type::Void => 0,
Type::I8 => 1,
Type::I16 => 2,
Type::I32 => 3,
Type::I64 => 4,
Type::F32 => 5,
Type::F64 => 6,
_ => unreachable!(),
};
unsafe {
invoke_trampoline(
entry,
raw_args.as_ptr(),
args.len(),
result_tag,
&mut result,
)
}
let result_ptr = &result as *const u64;
let v = match signature.1 {
Type::Void => None,
Type::Bool => Some(Value::Bool(unsafe { *(result_ptr as *const bool) })),
Type::I32 => Some(Value::I32(unsafe { *(result_ptr as *const i32) })),
Type::I64 => Some(Value::I64(unsafe { *(result_ptr as *const i64) })),
Type::F32 => Some(Value::F32(unsafe { *(result_ptr as *const f32) })),
Type::F64 => Some(Value::F64(unsafe { *(result_ptr as *const f64) })),
_ => unimplemented!(),
};
v
}
#[repr(C)]
struct RawValue(u64, u64);
extern "C" {
fn invoke_trampoline(
func: *const u8,
args: *const RawValue,
num_args: usize,
return_tag: u64,
result: *mut u64,
);
}
std::arch::global_asm! { r#"
.global invoke_trampoline
invoke_trampoline:
push rbp
mov rbp, rsp
push rbx
push r12
push r13
push r14
push r15
mov rax, rdi # func
mov r14, rsi # args
mov r15, rdx # num_args
shl r15, 4
add r15, r14 # r15 = end_address
xor r12, r12 # int args count = 0
xor r13, r13 # float args count = 0
mov rbx, rcx
push r8
set_arg:
cmp r14, r15 # Finished?
jge end
mov r11b, [r14] # Get value tag
add r14, 8 # Bump buffer pointer
cmp r11b, 5 # Check f32 arg
je set_f32_arg
cmp r11b, 6 # Check f64 arg
je set_f64_arg
set_int_arg:
# Check arg index
cmp r12, 0
je set_int_arg0
cmp r12, 1
je set_int_arg1
cmp r12, 2
je set_int_arg2
cmp r12, 3
je set_int_arg3
cmp r12, 4
je set_int_arg4
cmp r12, 5
je set_int_arg5
int3
set_int_arg0:
mov rdi, [r14]
jmp set_int_arg_end
set_int_arg1:
mov rsi, [r14]
jmp set_int_arg_end
set_int_arg2:
mov rdx, [r14]
jmp set_int_arg_end
set_int_arg3:
mov rcx, [r14]
jmp set_int_arg_end
set_int_arg4:
mov r8, [r14]
jmp set_int_arg_end
set_int_arg5:
mov r9, [r14]
jmp set_int_arg_end
set_int_arg_end:
add r12, 1
add r14, 8
jmp set_arg
set_f32_arg:
# Check arg index
cmp r13, 0
je set_f32_arg0
cmp r13, 1
je set_f32_arg1
cmp r13, 2
je set_f32_arg2
cmp r13, 3
je set_f32_arg3
cmp r13, 4
je set_f32_arg4
cmp r13, 5
je set_f32_arg5
int3
set_f32_arg0:
movss xmm0, [r14]
jmp set_f32_arg_end
set_f32_arg1:
movss xmm1, [r14]
jmp set_f32_arg_end
set_f32_arg2:
movss xmm2, [r14]
jmp set_f32_arg_end
set_f32_arg3:
movss xmm3, [r14]
jmp set_f32_arg_end
set_f32_arg4:
movss xmm4, [r14]
jmp set_f32_arg_end
set_f32_arg5:
movss xmm5, [r14]
jmp set_f32_arg_end
set_f32_arg_end:
add r13, 1
add r14, 8
jmp set_arg
set_f64_arg:
# Check arg index
cmp r13, 0
je set_f64_arg0
cmp r13, 1
je set_f64_arg1
cmp r13, 2
je set_f64_arg2
cmp r13, 3
je set_f64_arg3
cmp r13, 4
je set_f64_arg4
cmp r13, 5
je set_f64_arg5
int3
set_f64_arg0:
movsd xmm0, [r14]
jmp set_f64_arg_end
set_f64_arg1:
movsd xmm1, [r14]
jmp set_f64_arg_end
set_f64_arg2:
movsd xmm2, [r14]
jmp set_f64_arg_end
set_f64_arg3:
movsd xmm3, [r14]
jmp set_f64_arg_end
set_f64_arg4:
movsd xmm4, [r14]
jmp set_f64_arg_end
set_f64_arg5:
movsd xmm5, [r14]
jmp set_f64_arg_end
set_f64_arg_end:
add r13, 1
add r14, 8
jmp set_arg
end:
call rax
pop r8
cmp rbx, 5
je set_f32_result
cmp rbx, 6
je set_f64_result
set_int_result:
mov [r8], rax
jmp set_result_end
set_f32_result:
movss [r8], xmm0
jmp set_result_end
set_f64_result:
movsd [r8], xmm0
jmp set_result_end
set_result_end:
pop r15
pop r14
pop r13
pop r12
pop rbx
mov rsp, rbp
pop rbp
ret
"# }