feat(asm): symbol operands ("s") — direct call/branch to a function

A `"s"` input operand feeds a function/global symbol; the template's
%[name] emits the platform-mangled name, so `bl %[fn]` / `call %[fn]`
branches DIRECTLY to it (PC-relative, no register load — one fewer
indirection than register-indirect `blr`).

Lowering: an `"s"` input lowers its RHS normally (a function name →
`ptr @fn`); the rejection added last commit is removed. Emit: a symbol
operand is passed with its OWN llvm type (LLVMTypeOf) and no coercion —
the function value is a `ptr`, and the old coerce-to-register-int path
mistyped it and failed the verifier. New asmIsSymbol helper.

Verified on aarch64: examples/1656 (sx → asm → bl _cb → sx → 42); the
emitted asm is a direct `bl <_cb>` (objdump-confirmed), IR constraint
`...,s,...`(ptr @cb). Flipped 1656 from the rejection lock to a runnable
aarch64 example. zig build test green (665 corpus, 446 unit).
This commit is contained in:
agra
2026-06-16 08:24:53 +03:00
parent c187122531
commit 10f4137cbd
7 changed files with 64 additions and 46 deletions

View File

@@ -2366,17 +2366,11 @@ pub fn lowerAsmExpr(self: *Lowering, ae: *const ast.AsmExpr, span: ast.Span) Ref
var operand_ref: Ref = Ref.none;
var out_ty: TypeId = .void;
switch (op.role) {
.input => {
// Symbol operands (constraint `"s"`) — feed a function/global
// SYMBOL whose mangled name the template emits (e.g. a direct
// `bl %[fn]`). Not yet implemented; reject loudly rather than
// emit invalid IR (an LLVM-verifier crash). [Phase: symbol ops.]
if (std.mem.eql(u8, op.constraint, "s")) {
diags.addFmt(.err, span, "symbol asm operands (`\"s\"`) are not yet implemented", .{});
return self.emitPlaceholder("inline_asm");
}
operand_ref = self.lowerExpr(op.payload);
},
// Inputs (incl. symbol operands `"s"` — a function/global whose
// mangled name the template emits, e.g. a direct `bl %[fn]`). A
// symbol RHS (a function name) lowers to its address (`ptr @fn`);
// emit passes it with its own type so the backend prints the symbol.
.input => operand_ref = self.lowerExpr(op.payload),
.out_value => out_ty = self.resolveTypeWithBindings(op.payload),
.out_place => {
// Read-write (`+`) outputs tie an input to the output and seed