From 499d0e3d3146283feb7a20a2394ca34d0d116ffc Mon Sep 17 00:00:00 2001 From: Felix Ehlers Date: Tue, 14 Jul 2026 14:07:20 +0200 Subject: [PATCH 1/2] cgen: fix x86 segment-override memory operands in basic inline asm (fix #27777) --- vlib/v/gen/c/cgen.v | 5 +++- .../testdata/asm_segment_override.c.must_have | 4 +++ vlib/v/gen/c/testdata/asm_segment_override.vv | 30 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 vlib/v/gen/c/testdata/asm_segment_override.c.must_have create mode 100644 vlib/v/gen/c/testdata/asm_segment_override.vv diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 7d67009e360977..afb88903b60a25 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -6281,7 +6281,10 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) { } ast.AsmAddressing { if arg.segment != '' { - g.write('%%${arg.segment}:') + if !stmt.is_basic { + g.write('%') // escape percent with percent in extended assembly + } + g.write('%${arg.segment}:') } base := arg.base index := arg.index diff --git a/vlib/v/gen/c/testdata/asm_segment_override.c.must_have b/vlib/v/gen/c/testdata/asm_segment_override.c.must_have new file mode 100644 index 00000000000000..cbffd58437d40e --- /dev/null +++ b/vlib/v/gen/c/testdata/asm_segment_override.c.must_have @@ -0,0 +1,4 @@ +"mov %rsp, %gs:24\n\t" +"mov %gs:16, %rsp\n\t" +"mov %%fs:8, %%rax\n\t" +"mov %%rax, %[a]\n\t" diff --git a/vlib/v/gen/c/testdata/asm_segment_override.vv b/vlib/v/gen/c/testdata/asm_segment_override.vv new file mode 100644 index 00000000000000..5f3495888f099a --- /dev/null +++ b/vlib/v/gen/c/testdata/asm_segment_override.vv @@ -0,0 +1,30 @@ +fn basic_asm() { + // basic assembly (no operand lists) is passed through verbatim by the C + // compiler, so segment overrides must use a single, unescaped `%` + asm volatile amd64 { + mov gs:[24], rsp + mov rsp, gs:[16] + } +} + +fn extended_asm() u64 { + // extended assembly templates are processed by the C compiler, so + // segment overrides need the escaped `%%` form there + mut a := u64(0) + asm amd64 { + mov rax, fs:[8] + mov a, rax + ; =r (a) + ; ; rax + } + return a +} + +fn main() { + // guard the calls behind a runtime condition, so that running the + // program does not actually dereference the segmented addresses + if arguments().len > 1000 { + basic_asm() + println(extended_asm()) + } +} From 9ece160d2e1885a59d613252e323a79be1a55250 Mon Sep 17 00:00:00 2001 From: Felix Ehlers Date: Fri, 17 Jul 2026 06:12:40 +0200 Subject: [PATCH 2/2] parser: treat asm goto blocks as extended assembly, so template operands are escaped correctly --- .../testdata/asm_segment_override.c.must_have | 2 ++ vlib/v/gen/c/testdata/asm_segment_override.vv | 13 ++++++++++++ vlib/v/parser/asm.v | 20 ++++++++++--------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/vlib/v/gen/c/testdata/asm_segment_override.c.must_have b/vlib/v/gen/c/testdata/asm_segment_override.c.must_have index cbffd58437d40e..60b793af609f0a 100644 --- a/vlib/v/gen/c/testdata/asm_segment_override.c.must_have +++ b/vlib/v/gen/c/testdata/asm_segment_override.c.must_have @@ -2,3 +2,5 @@ "mov %gs:16, %rsp\n\t" "mov %%fs:8, %%rax\n\t" "mov %%rax, %[a]\n\t" +"mov %%gs:16, %%rax\n\t" +"jne %l[over]\n\t" diff --git a/vlib/v/gen/c/testdata/asm_segment_override.vv b/vlib/v/gen/c/testdata/asm_segment_override.vv index 5f3495888f099a..c63624c05ec373 100644 --- a/vlib/v/gen/c/testdata/asm_segment_override.vv +++ b/vlib/v/gen/c/testdata/asm_segment_override.vv @@ -20,11 +20,24 @@ fn extended_asm() u64 { return a } +fn goto_asm() { + // `asm goto` blocks are emitted as extended assembly even without any + // output/input/clobber operands, so they need the escaped `%%` form too + asm goto amd64 { + mov rax, gs:[16] + cmp rax, 0 + jne over + ; ; ; ; over + } + over: +} + fn main() { // guard the calls behind a runtime condition, so that running the // program does not actually dereference the segmented addresses if arguments().len > 1000 { basic_asm() println(extended_asm()) + goto_asm() } } diff --git a/vlib/v/parser/asm.v b/vlib/v/parser/asm.v index 91f3ec8871ed9e..6b1b30dadd29ec 100644 --- a/vlib/v/parser/asm.v +++ b/vlib/v/parser/asm.v @@ -289,15 +289,17 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { scope.end_pos = p.prev_tok.pos return ast.AsmStmt{ - arch: arch - is_goto: is_goto - is_volatile: is_volatile - templates: templates - output: output - input: input - clobbered: clobbered - pos: pos.extend(p.prev_tok.pos()) - is_basic: is_top_level || output.len + input.len + clobbered.len == 0 + arch: arch + is_goto: is_goto + is_volatile: is_volatile + templates: templates + output: output + input: input + clobbered: clobbered + pos: pos.extend(p.prev_tok.pos()) + // `asm goto` blocks are always emitted as extended assembly (they need the + // label section), even when they have no output/input/clobber operands + is_basic: is_top_level || (!is_goto && output.len + input.len + clobbered.len == 0) scope: scope global_labels: global_labels local_labels: local_labels