Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -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}:')
Comment on lines +6284 to +6287

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Escape segment prefixes in asm goto

In asm goto blocks that only provide the label list (for example asm goto amd64 { jmp gs:[16]; ; ; ; done }), the parser still leaves stmt.is_basic true because it only counts outputs/inputs/clobbers, but asm_stmt emits __asm__ goto (... : : : : label), which is extended asm. This branch therefore writes %gs: instead of %%gs:, and GCC/Clang parse %g as an operand escape (operand number missing after %-letter). Please base this escaping on whether the emitted C asm is extended, including stmt.is_goto, rather than is_basic alone.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex provided feedback for a valid problem here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@medvednikov the checks failing on other code, likely due to the new parser.

}
base := arg.base
index := arg.index
Expand Down
6 changes: 6 additions & 0 deletions vlib/v/gen/c/testdata/asm_segment_override.c.must_have
Original file line number Diff line number Diff line change
@@ -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"
43 changes: 43 additions & 0 deletions vlib/v/gen/c/testdata/asm_segment_override.vv
Original file line number Diff line number Diff line change
@@ -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()
}
}
20 changes: 11 additions & 9 deletions vlib/v/parser/asm.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading