Saltar al contenido principal

Ensambliado en línea

Introducción

El ensamblaje en línea de Wave se escribe usando asm { ... Se escribe en un bloque }`. Se pueden controlar directamente los registros, la memoria y los caminos de llamada del sistema dentro del código Wave.

Objetivos actualmente soportados:

  • Linux x86_64
  • Linux aarch64
  • macOS (Darwin) arm64
  • freestanding x86_64
  • freestanding aarch64
  • freestanding riscv64

Windows와 32비트 타깃은 아직 지원하지 않습니다.


Forma básica

asm puede usarse tanto como una declaración como una expresión.

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

Componentes:

  • Línea de cadena: instrucción de ensamblaje real
  • in(...): operando de entrada
  • out(...): operando de salida
  • clobber(...): pista de registros/estado/memoria destruidos

Declaración asm

Se usa como una declaración general cuando no se requiere un valor de retorno.

var ret: i64 = 0;
asm {
"mov rax, 1"
"syscall"
in("rdi") 1
in("rsi") msg_ptr
in("rdx") 20
out("rax") ret
}

out(...) puede admitir varios.


Expresión asm

Se puede usar como una expresión que genera valores directamente.

var result: i64 = asm {
"mov rax, 123"
out("rax") result
};

Aviso:

  • La expresión asm solo permite exactamente 1 out(...).

Restricción in(...) / out(...)

La cadena de in("..."), out("...") es una de las dos siguientes.

  1. Registro específico
  • 예: "rax", "rdi", "x0", "w1", "a0", "t0", "x10"
  1. Clase de restricción
  • Por ejemplo: "r", "m", "rm"

Ejemplo:

in("r") &buf
out("rax") ret

Destino de salida(out(...) se recomienda el siguiente patrón según la implementación actual para el target`.

  • Variable: out("rax") ret
  • Desreferencia de puntero: out("rax") deref p

clobber(...)

clobber(...) puede aceptar varios elementos a la vez y también se puede usar múltiples veces.

asm {
"xor rax, rax"
clobber("rax")
clobber("rcx", "rdx")
clobber("memory")
}

Elementos principales:

  • Registros: "rax", "x0", etc.
  • Especial: "memory", "cc" (normalización interna por objetivo)

El compilador añade automáticamente clobber básico en modo seguro conservador. (memory, flags/cc 계열 등; RISC-V freestanding에서는 주로 memory)


Marcadores de posición de operando ($0, $1, ...)

Se usa $N para referenciar operandos dentro de una cadena de comandos.

asm {
"mov QWORD PTR [$0], 777"
in("r") &buf
clobber("memory")
}

Nota:

  • El estilo %0 se convierte internamente en el estilo $0.

Rango de soporte actual para operandos de entrada

Los valores in(...) actualmente admiten las siguientes formas.

  • Identificador de variable
  • Literal de entero
  • Literal de cadena
  • &identifier
  • Identificador de desreferencia (deref identifier)
  • Literal de número entero/real negativo

Las expresiones generales complejas pueden estar limitadas, por lo que se recomienda transmitirlas a través de variables temporales cuando sea necesario.


Advertencias

El ensamblaje en línea esquiva parcialmente las protecciones del sistema de tipos. Asignaciones incorrectas de registros, conflictos de restricciones o la falta de clobber pueden provocar la generación de código incorrecto o errores en tiempo de ejecución.

Recomendaciones:

  • Primero, confirme el ABI y las convenciones de llamada de destino.
  • Administre explícitamente los registros de entrada/salida y clobber.
  • Declare clobber("memory") cuando manipule memoria directamente.