Skip to main content

Assembly ya ndani ya msimbo

Assembly ya ndani ya Wave huandikwa kwa kizuizi asm { ... }. Inafaa kwa msimbo wa kiwango cha chini kama kernel, vipakiaji vya UEFI, system calls, port I/O na udhibiti wa CPU.

Malengo ya sasa ni Linux x86_64/aarch64, Darwin x86_64/aarch64, Windows GNU x86_64, na freestanding x86_64/aarch64/riscv64. Malengo ya 32-bit bado hayajaungwa mkono.

Muundo wa msingi

asm {
"instruction"
in("constraint_or_reg") value
out("constraint_or_reg") target
clobber("item")
}

Mistari ya string ni maagizo ya assembly. in(...) hutangaza ingizo, out(...) hutangaza matokeo, na clobber(...) hutangaza hali inayobadilishwa na asm.

asm kama statement

asm kama statement hutumika pale ambapo thamani ya expression haihitajiki. Inaweza kuwa na matokeo kadhaa.

let mut ret: i64 = 0;
asm {
"mov rax, 39"
"syscall"
out("rax") ret
clobber("memory")
clobber("flags")
}

asm kama expression

asm kama expression huzalisha thamani na kwa sasa inahitaji out(...) moja tu. clobber("noreturn") hairuhusiwi katika asm ya expression.

let mut value: i64 = 0;
value = asm {
"mov rax, 123"
out("rax") value
};

Operands na constraints

Operand zinaweza kutumia register maalum au darasa la constraint. x86_64 hutumia rax, rbx, rcx, rdx, r8 ... r15; AArch64 hutumia x0 ... x30 na w0 ... w30; RISC-V hutumia a0, a1, t0, s0, ra, sp, xN. Madarasa ya kawaida ni r, m, rm, i, ri, im, irm. Register moja ya kimwili haiwezi kuwa operand na clobber kwa wakati mmoja.

Mkataba wa clobber

clobber("memory") humaanisha asm inaweza kusoma au kuandika memory. clobber("flags") na clobber("cc") humaanisha flags zimebadilishwa. clobber("stack") inahitajika stack au maagizo ya call/return yanapotumika. clobber("nostack") huahidi kutogusa stack. clobber("noreturn") humaanisha control hairudi kwenye block ya sasa. stack na nostack haziwezi kuunganishwa.

Nidhamu ya stack

asm ya kawaida haipaswi kubadilisha stack. call, push, pop, ret, matumizi ya moja kwa moja ya rsp/esp au sp, na maagizo yanayofanana yanahitaji clobber("stack"). Hata hivyo stack pointer lazima irejeshwe kabla ya kurudi.

asm {
"sub rsp, 8"
"add rsp, 8"
clobber("stack")
}

asm isiyorudi

Miruko isiyo ya moja kwa moja kama jmp rax, jmp r11, br x0, au jr ra inahitaji clobber("noreturn"). asm statement yenye clobber hii humaliza block ya IR kwa unreachable.

fun jump_to_kernel(entry: u64, boot_info: ptr<u8>, stack_top: u64) {
asm {
"mov rsp, rdx"
"and rsp, -16"
"mov rdi, rcx"
"jmp rbx"
in("rbx") entry
in("rcx") boot_info
in("rdx") stack_top
clobber("stack")
clobber("noreturn")
}
}

Lebo za ndani

Kuruka kwenda lebo ya ndani hubaki ndani ya njia ileile ya asm/control-flow na hakuhitaji noreturn.

asm {
"jmp 1f"
"1:"
}

Malengo ya output

Malengo thabiti ya output ni variables na deref ya variables za pointer. Kwa field au array, andika kwanza kwenye variable ya muda.

out("rax") value
out("rax") deref ptr

Mipaka

inline asm huchukuliwa kuwa na side effect kila wakati. Ubadilishaji mgumu wa stack bado unaweza kukataliwa. Function pointer na aina za calling convention zilizo wazi bado hazijathibitika, hivyo UEFI service calls zinaweza kutumia asm wrappers kwa sasa.