Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

native: fix convert_int_to_string, add comments, more verbose #23743

Merged
merged 10 commits into from
Feb 17, 2025
68 changes: 35 additions & 33 deletions vlib/v/gen/native/amd64.v
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ fn (op JumpOp) amd64() u16 {
fn (mut c Amd64) cjmp(op JumpOp) i32 {
c.g.write16(op.amd64())
pos := c.g.pos()
c.g.write32(placeholder)
c.g.write32(placeholder) // will get replaced by the right address
c.g.println('${op}')
return i32(pos)
}
Expand All @@ -466,7 +466,7 @@ fn (mut c Amd64) jmp(addr i32) i32 {
c.g.write8(0xe9)
pos := c.g.pos()
c.g.write32(addr) // 0xffffff
c.g.println('jmp')
c.g.println('jmp ${int(pos + addr).hex2()}')
// return the position of jump address for placeholder
return i32(pos)
}
Expand Down Expand Up @@ -603,7 +603,7 @@ fn (mut c Amd64) mov_store(regptr Amd64Register, reg Amd64Register, size Size) {
}
c.g.write8(if size == ._8 { i32(0x88) } else { i32(0x89) })
c.g.write8(i32(reg) % 8 * 8 + i32(regptr) % 8)
c.g.println('mov [${regptr}], ${reg}')
c.g.println('mov [${regptr}], ${reg} ; size:${size}bits')
}

fn (mut c Amd64) mov_reg_to_var(var Var, r Register, config VarConfig) {
Expand Down Expand Up @@ -885,7 +885,7 @@ fn (mut c Amd64) mov_var_to_reg(reg Register, var Var, config VarConfig) {
} else {
c.g.write8((0xff - offset + 1) % 0x100)
}
c.g.println('${instruction} ${reg}, ${size_str} PTR [rbp-${int(offset).hex2()}]')
c.g.println('${instruction} ${reg}, ${size_str} PTR [rbp-${int(offset).hex2()}] ; `${var.name}`')
}
GlobalVar {
// TODO
Expand Down Expand Up @@ -1263,6 +1263,7 @@ pub fn (mut c Amd64) test_reg(r Amd64Register) {

// return length in .rax of string pointed by given register
pub fn (mut c Amd64) inline_strlen(r Amd64Register) {
c.g.println('; inline_strlen: (reg:${r}) {')
c.mov_reg(Amd64Register.rdi, r)
c.mov(Amd64Register.rcx, -1)
c.mov(Amd64Register.eax, 0)
Expand All @@ -1271,6 +1272,7 @@ pub fn (mut c Amd64) inline_strlen(r Amd64Register) {
c.dec(.rcx)
c.mov_reg(Amd64Register.rax, Amd64Register.rcx)
c.g.println('strlen rax, ${r}')
c.g.println('; inline_strlen }')
}

pub fn (mut c Amd64) get_dllcall_addr(import_addr i64) i64 {
Expand All @@ -1296,6 +1298,7 @@ pub fn (mut c Amd64) dllcall(symbol string) {
}

fn (mut c Amd64) gen_print(s string, fd i32) {
c.g.println('; print: (fd:${fd} s:\'${s}\') {')
if c.g.pref.os == .windows {
c.sub(.rsp, 0x38)
c.mov(Amd64Register.rcx, -10 - fd)
Expand All @@ -1317,9 +1320,11 @@ fn (mut c Amd64) gen_print(s string, fd i32) {
c.mov(Amd64Register.edx, i32(s.len)) // len
c.syscall()
}
c.g.println('; print }')
}

pub fn (mut c Amd64) gen_print_reg(r Register, n i32, fd i32) {
c.g.println('; print_reg: (reg:${r} fd:${fd} len:${n}) {')
str_reg := if c.g.pref.os == .windows { Amd64Register.rdx } else { Amd64Register.rsi }
len_reg := if c.g.pref.os == .windows { Amd64Register.r8 } else { Amd64Register.rdx }
c.mov_reg(str_reg, r)
Expand Down Expand Up @@ -1347,6 +1352,7 @@ pub fn (mut c Amd64) gen_print_reg(r Register, n i32, fd i32) {
c.mov(Amd64Register.edi, fd)
c.syscall()
}
c.g.println('; print_reg }')
}

pub fn (mut c Amd64) gen_exit(expr ast.Expr) {
Expand Down Expand Up @@ -1590,10 +1596,9 @@ fn (mut c Amd64) div_reg(a Amd64Register, b Amd64Register) {
c.g.write8(0xf8)
}
.rbx {
c.mov(Amd64Register.edx, 0)
c.g.write8(0x48)
c.g.write8(0xf7)
c.g.write8(0xfb) // idiv ebx
c.g.write8(0xfb)
}
.rdx {
c.g.write8(0x48)
Expand Down Expand Up @@ -1937,8 +1942,8 @@ pub fn (mut c Amd64) call_fn(node ast.CallExpr) {
}

fn (mut c Amd64) call_builtin(name Builtin) i64 {
call_addr := c.call(0)
c.g.println('call builtin `${name}`')
c.g.println('; call builtin `${name}`: (the 0 will get replaced)')
call_addr := c.call(0) // the 0 will get replaced by the right addr when the function will be generated
return call_addr
}

Expand Down Expand Up @@ -3478,6 +3483,13 @@ fn (mut c Amd64) convert_int_to_string(a Register, b Register) {
c.g.write8(0x30)
c.g.println("mov BYTE PTR [rdi], '0'")

// null terminate the string
c.inc(.rdi)
c.g.write8(0xc6)
c.g.write8(0x07)
c.g.write8(0x0)
c.g.println('mov BYTE PTR [rdi], `\0`')

end_label := c.g.labels.new_label()
end_jmp_addr := c.jmp(0)
c.g.labels.patches << LabelPatch{
Expand Down Expand Up @@ -3513,32 +3525,18 @@ fn (mut c Amd64) convert_int_to_string(a Register, b Register) {
c.g.labels.addrs[skip_minus_label] = c.g.pos()
c.g.println('; label ${skip_minus_label}')

c.mov_reg(Amd64Register.r12, Amd64Register.rdi) // copy the buffer position to r12
c.mov_reg(Amd64Register.r12, Amd64Register.rdi) // copy the buffer position (start of the number without `-`) to r12

loop_label := c.g.labels.new_label()
loop_start := c.g.pos()
c.g.println('; label ${loop_label}')

c.push(Amd64Register.rax)

c.mov(Amd64Register.rdx, 0)
c.mov(Amd64Register.rdx, 0) // upperhalf of the dividend
c.mov(Amd64Register.rbx, 10)
c.div_reg(.rax, .rbx)
c.add8(.rdx, '0'[0])
c.div_reg(.rax, .rbx) // rax will be the result of the division
c.add8(.rdx, i32(`0`)) // rdx is the remainder, add 48 to convert it into it's ascii representation

c.g.write8(0x66)
c.g.write8(0x89)
c.g.write8(0x17)
c.g.println('mov BYTE PTR [rdi], rdx')

// divide the integer in rax by 10 for next iteration
c.pop(.rax)
c.mov(Amd64Register.rbx, 10)
c.cdq()
c.g.write8(0x48)
c.g.write8(0xf7)
c.g.write8(0xfb)
c.g.println('idiv rbx')
c.mov_store(.rdi, .rdx, ._8)

// go to the next character
c.inc(.rdi)
Expand All @@ -3550,9 +3548,15 @@ fn (mut c Amd64) convert_int_to_string(a Register, b Register) {
id: loop_label
pos: loop_cjmp_addr
}
c.g.println('; jump to label ${skip_minus_label}')
c.g.println('; jump to label ${loop_label}')
c.g.labels.addrs[loop_label] = loop_start

// null terminate the string
c.g.write8(0xc6)
c.g.write8(0x07)
c.g.write8(0x0)
c.g.println('mov BYTE PTR [rdi], `\0`')

// after all was converted, reverse the string
reg := c.g.get_builtin_arg_reg(.reverse_string, 0) as Amd64Register
c.mov_reg(reg, Amd64Register.r12)
Expand All @@ -3569,8 +3573,6 @@ fn (mut c Amd64) reverse_string(r Register) {
c.mov_reg(Amd64Register.rdi, reg)
}

c.mov(Amd64Register.eax, 0)

c.g.write8(0x48)
c.g.write8(0x8d)
c.g.write8(0x48)
Expand All @@ -3579,9 +3581,9 @@ fn (mut c Amd64) reverse_string(r Register) {

c.mov_reg(Amd64Register.rsi, Amd64Register.rdi)

c.g.write8(0xf2)
c.g.write8(0xae)
c.g.println('repnz scas al, BYTE PTR es:[rdi]')
// search for null at end of string
c.mov(Amd64Register.eax, 0)
c.cld_repne_scasb()

c.sub8(.rdi, 0x2)
c.cmp_reg(.rdi, .rsi)
Expand Down
10 changes: 8 additions & 2 deletions vlib/v/gen/native/gen.v
Original file line number Diff line number Diff line change
Expand Up @@ -856,9 +856,11 @@ fn (mut g Gen) allocate_string(s string, opsize i32, typ RelocType) i32 {
return str_pos
}

// allocates a buffer variable: name, size of stored type (nb of bytes), nb of items
fn (mut g Gen) allocate_array(name string, size i32, items i32) i32 {
pos := g.code_gen.allocate_var(name, size, items)
g.stack_var_pos += (size * items)
g.println('; allocate array `${name}` item-size:${size} items:${items}:')
pos := g.code_gen.allocate_var(name, size, items) // store the lenght of the array on the stack
g.stack_var_pos += (size * items) // reserve space on the stack for the items
return pos
}

Expand Down Expand Up @@ -945,6 +947,7 @@ fn (mut g Gen) eval_escape_codes(str string) string {
}

fn (mut g Gen) gen_to_string(reg Register, typ ast.Type) {
g.println('; to_string (reg:${reg}) {')
if typ.is_int() {
buffer := g.allocate_array('itoa-buffer', 1, 32) // 32 characters should be enough
g.code_gen.lea_var_to_reg(g.get_builtin_arg_reg(.int_to_string, 1), buffer)
Expand All @@ -969,9 +972,11 @@ fn (mut g Gen) gen_to_string(reg Register, typ ast.Type) {
} else {
g.n_error('int-to-string conversion not implemented for type ${typ}')
}
g.println('; to_string }')
}

fn (mut g Gen) gen_var_to_string(reg Register, expr ast.Expr, var Var, config VarConfig) {
g.println('; var_to_string {')
typ := g.get_type_from_var(var)
if typ == ast.rune_type_idx {
buffer := g.code_gen.allocate_var('rune-buffer', 8, 0)
Expand All @@ -990,6 +995,7 @@ fn (mut g Gen) gen_var_to_string(reg Register, expr ast.Expr, var Var, config Va
} else {
g.n_error('int-to-string conversion not implemented for type ${typ}')
}
g.println('; var_to_string }')
}

fn (mut g Gen) is_used_by_main(node ast.FnDecl) bool {
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/gen/native/tests/print.vv
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ fn test_interpolated() {
println('num: ${num}; bool: ${boool}')
}

fn test_intermediate() {
c := 0
a := 'a'
println(c)
}

fn main() {
test_stdout()
test_stderr()
Expand All @@ -113,4 +119,5 @@ fn main() {
test_exprs()
test_sizeof()
test_interpolated()
test_intermediate()
}
1 change: 1 addition & 0 deletions vlib/v/gen/native/tests/print.vv.out
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ false true
sizeof: 4, 12
this is an interpolated string
num: 42; bool: true
0
Loading