test(asm): x86_64 cross-arch siblings for place + symbol operands

Adds ir-only x86_64 examples mirroring the aarch64 feature examples, so
each emit path is locked on both arches:
- 1657 read-write `+`  → "incq ${0}", "=r,0" (tied input)
- 1658 indirect `=*m`   → "movq $$42, ${0}", "=*m"(ptr elementtype i64)
- 1659 symbol `"s"`     → "call ${2:P}", direct call to an exported sx fn

Each is x86-pinned (ir-only on this aarch64 host — the .ir is the
assertion; runs on x86_64-linux, main returns 0 on success / 1 if the
asm misbehaved). x86 templates validated by cross-emitting an object
(LLVM's integrated assembler accepts them; objdump confirms 1659 is a
direct `call` reloc to cb). Note: x86 direct calls need the `P` operand
modifier (`%[fn:P]`); aarch64 `bl %[fn]` needs none. Pure additive
locks, no compiler change. zig build test green (668 corpus, 446 unit).
This commit is contained in:
agra
2026-06-16 08:36:33 +03:00
parent a0face7571
commit 17e3b91eb9
15 changed files with 135 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
// ASM stream — read-write (`+`) place output on x86_64 (cross-arch sibling of
// the aarch64 1650). `incq %[v]` reads the operand register, increments it, and
// the result is stored back through the place. Locks the x86 lowering of `+`:
// an output `=r` plus a tied input (`=r,0`) seeded with the place's value.
// x86-pinned via `.build`: ir-only here (the `.ir` is the assertion), runs
// natively on x86_64-linux (main returns 0 on success, 1 if the asm misbehaved).
bump :: () -> i64 {
x : i64 = 41;
asm volatile { "incq %[v]", [v] "+r" -> @x };
return x; // 42
}
main :: () -> i64 { if bump() != 42 { return 1; } return 0; }

View File

@@ -0,0 +1,12 @@
// ASM stream — indirect-memory (`=*m`) place output on x86_64 (cross-arch sibling
// of the aarch64 1652). `movq $42, %[out]` stores straight through the place's
// address — the address is passed as an opaque `ptr` with an `elementtype(i64)`
// attribute, no return slot. Note `$42`: a literal `$` in the template is escaped
// to LLVM's `$$` and emitted back as `$42` (an x86 immediate). x86-pinned;
// ir-only here, runs on x86_64-linux.
poke :: () -> i64 {
x : i64 = 0;
asm volatile { "movq $42, %[out]", [out] "=*m" -> @x };
return x; // 42
}
main :: () -> i64 { if poke() != 42 { return 1; } return 0; }

View File

@@ -0,0 +1,18 @@
// ASM stream — symbol operand (`"s"`) on x86_64 (cross-arch sibling of the
// aarch64 1656). A DIRECT `call` to an `export`ed sx function by symbol. x86
// direct calls need the `P` operand modifier (`%[fn:P]` → `${N:P}`) — the GCC
// `%P0` call-target idiom — whereas aarch64 `bl %[fn]` needs none. The backend
// emits the platform-mangled name (`call cb` on Linux). x86-pinned; ir-only
// here, runs on x86_64-linux. Round trip: sx → asm → call cb → sx → 42.
cb :: (n: i64) -> i64 export "cb" { return n + 1; }
tramp :: (n: i64) -> i64 {
return asm volatile {
"call %[fn:P]",
[ret] "={rax}" -> i64,
"{rdi}" = n, // arg in rdi (SysV)
[fn] "s" = cb, // symbol operand → direct `call cb`
clobbers(.rcx, .rdx, .rsi, .r8, .r9, .r10, .r11, .memory),
};
}
main :: () -> i64 { if tramp(41) != 42 { return 1; } return 0; }

View File

@@ -0,0 +1 @@
{ "target": "x86_64-linux" }

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,26 @@
; Function Attrs: nounwind
define internal i64 @bump() #0 {
entry:
%alloca = alloca i64, align 8
store i64 41, ptr %alloca, align 8
%asm.rw.seed = load i64, ptr %alloca, align 8
%asm = call i64 asm sideeffect "incq ${0}", "=r,0"(i64 %asm.rw.seed)
store i64 %asm, ptr %alloca, align 8
%load = load i64, ptr %alloca, align 8
ret i64 %load
}
; Function Attrs: nounwind
define i32 @main() #0 {
entry:
%call = call i64 @bump()
%icmp = icmp ne i64 %call, 42
br i1 %icmp, label %if.then.0, label %if.merge.1
if.then.0: ; preds = %entry
ret i32 1
if.merge.1: ; preds = %entry
ret i32 0
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@
{ "target": "x86_64-linux" }

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,24 @@
; Function Attrs: nounwind
define internal i64 @poke() #0 {
entry:
%alloca = alloca i64, align 8
store i64 0, ptr %alloca, align 8
call void asm sideeffect "movq $$42, ${0}", "=*m"(ptr elementtype(i64) %alloca)
%load = load i64, ptr %alloca, align 8
ret i64 %load
}
; Function Attrs: nounwind
define i32 @main() #0 {
entry:
%call = call i64 @poke()
%icmp = icmp ne i64 %call, 42
br i1 %icmp, label %if.then.0, label %if.merge.1
if.then.0: ; preds = %entry
ret i32 1
if.merge.1: ; preds = %entry
ret i32 0
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@
{ "target": "x86_64-linux" }

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,34 @@
; Function Attrs: nounwind
define i64 @cb(i64 %0) #0 {
entry:
%alloca = alloca i64, align 8
store i64 %0, ptr %alloca, align 8
%load = load i64, ptr %alloca, align 8
%add = add i64 %load, 1
ret i64 %add
}
; Function Attrs: nounwind
define internal i64 @tramp(i64 %0) #0 {
entry:
%alloca = alloca i64, align 8
store i64 %0, ptr %alloca, align 8
%load = load i64, ptr %alloca, align 8
%asm = call i64 asm sideeffect "call ${2:P}", "={rax},{rdi},s,~{rcx},~{rdx},~{rsi},~{r8},~{r9},~{r10},~{r11},~{memory}"(i64 %load, ptr @cb)
ret i64 %asm
}
; Function Attrs: nounwind
define i32 @main() #0 {
entry:
%call = call i64 @tramp(i64 41)
%icmp = icmp ne i64 %call, 42
br i1 %icmp, label %if.then.0, label %if.merge.1
if.then.0: ; preds = %entry
ret i32 1
if.merge.1: ; preds = %entry
ret i32 0
}

View File

@@ -0,0 +1 @@