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..60b793af609f0a --- /dev/null +++ b/vlib/v/gen/c/testdata/asm_segment_override.c.must_have @@ -0,0 +1,6 @@ +"mov %rsp, %gs:24\n\t" +"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 new file mode 100644 index 00000000000000..c63624c05ec373 --- /dev/null +++ b/vlib/v/gen/c/testdata/asm_segment_override.vv @@ -0,0 +1,43 @@ +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 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