Skip to content

Commit f9a4f8f

Browse files
authored
native: implement for in string for amd64 (#24613)
1 parent 42a9927 commit f9a4f8f

File tree

9 files changed

+128
-10
lines changed

9 files changed

+128
-10
lines changed

vlib/v/gen/native/amd64.v

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ fn (mut c Amd64) cmp(reg Amd64Register, size Size, val i64) {
212212
c.g.println('cmp ${reg}, ${val}')
213213
}
214214

215+
fn (mut c Amd64) cmp_reg2(reg Register, reg2 Register) {
216+
c.cmp_reg(reg as Amd64Register, reg2 as Amd64Register)
217+
}
218+
215219
// `cmp rax, rbx`
216220
fn (mut c Amd64) cmp_reg(reg Amd64Register, reg2 Amd64Register) {
217221
match reg {
@@ -1101,8 +1105,12 @@ fn (mut c Amd64) push(r Register) {
11011105
c.g.write8(0x50 + i32(reg) - 8)
11021106
}
11031107
c.is_16bit_aligned = !c.is_16bit_aligned
1104-
c.g.println('push ${reg}')
11051108
c.g.stack_depth++
1109+
c.g.println('push ${reg}; stack_depth:${c.g.stack_depth}')
1110+
}
1111+
1112+
fn (mut c Amd64) pop2(r Register) {
1113+
c.pop(r as Amd64Register)
11061114
}
11071115

11081116
pub fn (mut c Amd64) pop(reg Amd64Register) {
@@ -1111,8 +1119,8 @@ pub fn (mut c Amd64) pop(reg Amd64Register) {
11111119
}
11121120
c.g.write8(0x58 + i32(reg) % 8)
11131121
c.is_16bit_aligned = !c.is_16bit_aligned
1114-
c.g.println('pop ${reg}')
11151122
c.g.stack_depth--
1123+
c.g.println('pop ${reg} ; stack_depth:${c.g.stack_depth}')
11161124
}
11171125

11181126
pub fn (mut c Amd64) sub8(reg Amd64Register, val i32) {
@@ -3620,7 +3628,7 @@ pub fn (mut c Amd64) allocate_var(name string, size i32, initial_val Number) i32
36203628
}
36213629

36223630
// println('allocate_var(size=$size, initial_val=$initial_val)')
3623-
c.g.println('mov [rbp-${int(n).hex2()}], ${initial_val} ; Allocate var `${name}`')
3631+
c.g.println('mov [rbp-${int(n).hex2()}], ${initial_val} ; Allocate var `${name}` size: ${size}')
36243632
return c.g.stack_var_pos
36253633
}
36263634

vlib/v/gen/native/arm64.v

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,14 @@ pub fn (mut c Arm64) add_reg2(r Register, r2 Register) {
535535
panic('Arm64.add_reg2() not implemented')
536536
}
537537

538+
fn (mut c Arm64) pop2(r Register) {
539+
panic('Arm64.pop2() not implemented')
540+
}
541+
542+
fn (mut c Arm64) cmp_reg2(reg Register, reg2 Register) {
543+
panic('Arm64.add_reg2() not implemented')
544+
}
545+
538546
fn (mut c Arm64) create_string_struct(typ ast.Type, name string, str string) {
539547
panic('Arm64.add_reg2() not implemented')
540548
}

vlib/v/gen/native/blacklist.v

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@ const whitelist = {
4646
'string.is_capital': false
4747
'string.is_ascii': false
4848
'string.is_identifier': false
49-
// 'string.is_blank': false need for in
49+
'string.is_blank': false
50+
'string.indent_width': false
51+
'string.index_u8': false
52+
'string.last_index': true
53+
'string.last_index_u8': false
54+
'string.contains_u8': false
5055
}
5156

5257
fn (g &Gen) is_blacklisted(name string, is_builtin bool) bool {

vlib/v/gen/native/gen.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ mut:
9595
cmp_to_stack_top(r Register)
9696
cmp_var_reg(var Var, reg Register, config VarConfig)
9797
cmp_var(var Var, val i32, config VarConfig)
98+
cmp_reg2(reg Register, reg2 Register)
9899
cmp_zero(reg Register)
99100
convert_bool_to_string(r Register)
100101
convert_int_to_string(a Register, b Register)
@@ -131,6 +132,7 @@ mut:
131132
mov64(r Register, val Number)
132133
movabs(reg Register, val i64)
133134
patch_relative_jmp(pos i32, addr i64)
135+
pop2(r Register)
134136
prefix_expr(node ast.PrefixExpr)
135137
push(r Register)
136138
ret()

vlib/v/gen/native/stmt.c.v

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,14 +255,14 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
255255
}
256256

257257
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { // Work on that
258+
main_reg := g.code_gen.main_reg()
258259
if node.is_range {
259260
g.println('; for ${node.val_var} in range {')
260261
// for a in node.cond .. node.high {
261262

262263
i := g.code_gen.allocate_var(node.val_var, 8, i64(0)) // iterator variable
263264
g.println('; evaluate node.cond for lower bound:')
264265
g.expr(node.cond) // outputs the lower loop bound (initial value) to the main reg
265-
main_reg := g.code_gen.main_reg()
266266
g.println('; move the result to i')
267267
g.code_gen.mov_reg_to_var(LocalVar{i, ast.i64_type_idx, node.val_var}, main_reg) // i = node.cond // initial value
268268

@@ -299,11 +299,79 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { // Work on that
299299
g.labels.addrs[end_label] = g.pos()
300300
g.println('; label ${end_label} (end_label)')
301301
g.println('; for ${node.val_var} in range }')
302+
} else if node.kind == .string {
303+
g.println('; for ${node.val_var} in string {')
304+
// for c in my_string {
305+
306+
key_var := if node.key_var == '' { 'i' } else { node.key_var }
307+
i := g.code_gen.allocate_var(key_var, 8, i64(0)) // iterator variable
308+
c := g.code_gen.allocate_var(node.val_var, 1, i64(0)) // char variable
309+
310+
g.expr(node.cond) // get the address of the string variable
311+
g.code_gen.mov_deref(Amd64Register.rdx, main_reg, ast.charptr_type)
312+
g.println('; push address of the string chars')
313+
g.code_gen.push(Amd64Register.rdx) // address of the string
314+
g.code_gen.add(main_reg, g.get_field_offset(ast.string_type, 'len'))
315+
g.println('; push address of the len:')
316+
g.code_gen.push(main_reg) // address of the len
317+
318+
start := g.pos() // label-begin:
319+
320+
g.println('; check iterator against upper loop bound')
321+
g.code_gen.mov_var_to_reg(main_reg, LocalVar{i, ast.i64_type_idx, key_var})
322+
g.println('; pop address of the len:')
323+
g.code_gen.pop2(Amd64Register.rdx)
324+
g.println('; push address of the len:')
325+
g.code_gen.push(Amd64Register.rdx) // len
326+
g.code_gen.mov_deref(Amd64Register.rdx, Amd64Register.rdx, ast.int_type)
327+
g.code_gen.cmp_reg2(main_reg, Amd64Register.rdx)
328+
jump_addr := g.code_gen.cjmp(.jge) // leave loop i >= len
329+
330+
g.println('; pop address of the len:')
331+
g.code_gen.pop2(Amd64Register.rdx) // len
332+
g.println('; pop address of the string chars')
333+
g.code_gen.pop2(Amd64Register.rax) // address of the string
334+
g.println('; push address of the string chars')
335+
g.code_gen.push(Amd64Register.rax)
336+
g.println('; push address of the len:')
337+
g.code_gen.push(Amd64Register.rdx) // len
338+
339+
g.code_gen.mov_var_to_reg(Amd64Register.rdx, LocalVar{i, ast.i64_type_idx, key_var})
340+
g.code_gen.add_reg2(Amd64Register.rax, Amd64Register.rdx)
341+
g.code_gen.mov_deref(Amd64Register.rax, Amd64Register.rax, ast.u8_type_idx)
342+
g.code_gen.mov_reg_to_var(LocalVar{c, ast.u8_type_idx, node.val_var}, Amd64Register.rax) // store the char
343+
344+
end_label := g.labels.new_label()
345+
g.labels.patches << LabelPatch{
346+
id: end_label
347+
pos: jump_addr
348+
}
349+
g.println('; jump to label ${end_label} (end_label)')
350+
351+
start_label := g.labels.new_label() // used for continue
352+
g.labels.branches << BranchLabel{
353+
name: node.label
354+
start: start_label
355+
end: end_label
356+
}
357+
g.stmts(node.stmts) // writes the actual body of the loop
358+
359+
g.labels.addrs[start_label] = g.pos() // used for continue (continue: jump before the inc)
360+
g.println('; label ${start_label} (continue_label)')
361+
362+
g.code_gen.inc_var(LocalVar{i, ast.i64_type_idx, key_var})
363+
g.labels.branches.pop()
364+
g.code_gen.jmp_back(start) // loops
365+
366+
g.labels.addrs[end_label] = g.pos()
367+
g.code_gen.pop2(Amd64Register.rdx) // len
368+
g.code_gen.pop2(Amd64Register.rax) // address of the string
369+
g.println('; label ${end_label} (end_label)')
370+
g.println('; for ${node.val_var} in string }')
302371
/*
303372
} else if node.kind == .array {
304373
} else if node.kind == .array_fixed {
305374
} else if node.kind == .map {
306-
} else if node.kind == .string {
307375
} else if node.kind == .struct {
308376
} else if it.kind in [.array, .string] || it.cond_type.has_flag(.variadic) {
309377
} else if it.kind == .map {

vlib/v/gen/native/tests/builtin.vv

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ assert !a.is_ascii()
2222
assert '_9'.is_identifier() == true
2323
assert 'a 9'.is_identifier() == false
2424
assert 't'.is_identifier() == true
25-
/*
2625
assert ''.is_blank()
2726
assert ' '.is_blank()
2827
assert ' \t'.is_blank()
@@ -48,18 +47,18 @@ assert !a.is_ascii()
4847
assert 'abc'.index_u8(`A`) == -1
4948
assert 'abc'.index_u8(`B`) == -1
5049
assert 'abc'.index_u8(`C`) == -1
50+
/*
5151
assert 'abcabca'.last_index('ca')? == 5
5252
assert 'abcabca'.last_index('ab')? == 3
5353
assert 'abcabca'.last_index('b')? == 4
5454
assert 'Zabcabca'.last_index('Z')? == 0
5555
x := 'Zabcabca'.last_index('Y')
5656
assert x == none
57-
// TODO: `assert 'Zabcabca'.index_last('Y') == none` is a cgen error, 2023/12/04
57+
*/
5858
assert 'abcabca'.last_index_u8(`a`) == 6
5959
assert 'abcabca'.last_index_u8(`c`) == 5
6060
assert 'abcabca'.last_index_u8(`b`) == 4
6161
assert 'Zabcabca'.last_index_u8(`Z`) == 0
62-
//
6362
assert 'abc'.last_index_u8(`d`) == -1
6463
assert 'abc'.last_index_u8(`A`) == -1
6564
assert 'abc'.last_index_u8(`B`) == -1
@@ -69,6 +68,7 @@ assert !a.is_ascii()
6968
assert 'abc abca'.contains_u8(`c`)
7069
assert 'abc abca'.contains_u8(` `)
7170
assert !'abc abca'.contains_u8(`A`)
71+
/*
7272
assert 'Abcd'.camel_to_snake() == 'abcd'
7373
assert 'aBcd'.camel_to_snake() == 'a_bcd'
7474
assert 'AAbb'.camel_to_snake() == 'aa_bb'

vlib/v/gen/native/tests/linux.vv

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
fn test_for_c_in_string() {
3+
$if windows {
4+
println('0')
5+
println('97')
6+
println('1')
7+
println('98')
8+
println('2')
9+
println('99')
10+
} $else {
11+
s := 'abc'
12+
for i, c in s {
13+
println(i)
14+
println(int(c))
15+
}
16+
}
17+
}
18+
19+
fn main() {
20+
test_for_c_in_string()
21+
}

vlib/v/gen/native/tests/linux.vv.out

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
0
2+
97
3+
1
4+
98
5+
2
6+
99

vlib/v/gen/native/tests/native_test.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ fn test_native() {
4949
continue
5050
}
5151
}
52-
if test == 'fibonacci_native.vv' {
52+
if test == 'fibonacci_native.vv' || test.contains('linux') {
5353
if user_os == 'windows' {
5454
println('>>> SKIPPING ${test} on windows for now')
5555
continue

0 commit comments

Comments
 (0)