native: implement for in string for amd64 (#24613)

This commit is contained in:
Eliyaan (Nopana) 2025-05-31 12:38:05 +02:00 committed by GitHub
parent 42a992775e
commit f9a4f8f19f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 128 additions and 10 deletions

View file

@ -212,6 +212,10 @@ fn (mut c Amd64) cmp(reg Amd64Register, size Size, val i64) {
c.g.println('cmp ${reg}, ${val}')
}
fn (mut c Amd64) cmp_reg2(reg Register, reg2 Register) {
c.cmp_reg(reg as Amd64Register, reg2 as Amd64Register)
}
// `cmp rax, rbx`
fn (mut c Amd64) cmp_reg(reg Amd64Register, reg2 Amd64Register) {
match reg {
@ -1101,8 +1105,12 @@ fn (mut c Amd64) push(r Register) {
c.g.write8(0x50 + i32(reg) - 8)
}
c.is_16bit_aligned = !c.is_16bit_aligned
c.g.println('push ${reg}')
c.g.stack_depth++
c.g.println('push ${reg}; stack_depth:${c.g.stack_depth}')
}
fn (mut c Amd64) pop2(r Register) {
c.pop(r as Amd64Register)
}
pub fn (mut c Amd64) pop(reg Amd64Register) {
@ -1111,8 +1119,8 @@ pub fn (mut c Amd64) pop(reg Amd64Register) {
}
c.g.write8(0x58 + i32(reg) % 8)
c.is_16bit_aligned = !c.is_16bit_aligned
c.g.println('pop ${reg}')
c.g.stack_depth--
c.g.println('pop ${reg} ; stack_depth:${c.g.stack_depth}')
}
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
}
// println('allocate_var(size=$size, initial_val=$initial_val)')
c.g.println('mov [rbp-${int(n).hex2()}], ${initial_val} ; Allocate var `${name}`')
c.g.println('mov [rbp-${int(n).hex2()}], ${initial_val} ; Allocate var `${name}` size: ${size}')
return c.g.stack_var_pos
}

View file

@ -535,6 +535,14 @@ pub fn (mut c Arm64) add_reg2(r Register, r2 Register) {
panic('Arm64.add_reg2() not implemented')
}
fn (mut c Arm64) pop2(r Register) {
panic('Arm64.pop2() not implemented')
}
fn (mut c Arm64) cmp_reg2(reg Register, reg2 Register) {
panic('Arm64.add_reg2() not implemented')
}
fn (mut c Arm64) create_string_struct(typ ast.Type, name string, str string) {
panic('Arm64.add_reg2() not implemented')
}

View file

@ -46,7 +46,12 @@ const whitelist = {
'string.is_capital': false
'string.is_ascii': false
'string.is_identifier': false
// 'string.is_blank': false need for in
'string.is_blank': false
'string.indent_width': false
'string.index_u8': false
'string.last_index': true
'string.last_index_u8': false
'string.contains_u8': false
}
fn (g &Gen) is_blacklisted(name string, is_builtin bool) bool {

View file

@ -95,6 +95,7 @@ mut:
cmp_to_stack_top(r Register)
cmp_var_reg(var Var, reg Register, config VarConfig)
cmp_var(var Var, val i32, config VarConfig)
cmp_reg2(reg Register, reg2 Register)
cmp_zero(reg Register)
convert_bool_to_string(r Register)
convert_int_to_string(a Register, b Register)
@ -131,6 +132,7 @@ mut:
mov64(r Register, val Number)
movabs(reg Register, val i64)
patch_relative_jmp(pos i32, addr i64)
pop2(r Register)
prefix_expr(node ast.PrefixExpr)
push(r Register)
ret()

View file

@ -255,6 +255,7 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
}
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { // Work on that
main_reg := g.code_gen.main_reg()
if node.is_range {
g.println('; for ${node.val_var} in range {')
// for a in node.cond .. node.high {
@ -262,7 +263,6 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { // Work on that
i := g.code_gen.allocate_var(node.val_var, 8, i64(0)) // iterator variable
g.println('; evaluate node.cond for lower bound:')
g.expr(node.cond) // outputs the lower loop bound (initial value) to the main reg
main_reg := g.code_gen.main_reg()
g.println('; move the result to i')
g.code_gen.mov_reg_to_var(LocalVar{i, ast.i64_type_idx, node.val_var}, main_reg) // i = node.cond // initial value
@ -299,11 +299,79 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { // Work on that
g.labels.addrs[end_label] = g.pos()
g.println('; label ${end_label} (end_label)')
g.println('; for ${node.val_var} in range }')
} else if node.kind == .string {
g.println('; for ${node.val_var} in string {')
// for c in my_string {
key_var := if node.key_var == '' { 'i' } else { node.key_var }
i := g.code_gen.allocate_var(key_var, 8, i64(0)) // iterator variable
c := g.code_gen.allocate_var(node.val_var, 1, i64(0)) // char variable
g.expr(node.cond) // get the address of the string variable
g.code_gen.mov_deref(Amd64Register.rdx, main_reg, ast.charptr_type)
g.println('; push address of the string chars')
g.code_gen.push(Amd64Register.rdx) // address of the string
g.code_gen.add(main_reg, g.get_field_offset(ast.string_type, 'len'))
g.println('; push address of the len:')
g.code_gen.push(main_reg) // address of the len
start := g.pos() // label-begin:
g.println('; check iterator against upper loop bound')
g.code_gen.mov_var_to_reg(main_reg, LocalVar{i, ast.i64_type_idx, key_var})
g.println('; pop address of the len:')
g.code_gen.pop2(Amd64Register.rdx)
g.println('; push address of the len:')
g.code_gen.push(Amd64Register.rdx) // len
g.code_gen.mov_deref(Amd64Register.rdx, Amd64Register.rdx, ast.int_type)
g.code_gen.cmp_reg2(main_reg, Amd64Register.rdx)
jump_addr := g.code_gen.cjmp(.jge) // leave loop i >= len
g.println('; pop address of the len:')
g.code_gen.pop2(Amd64Register.rdx) // len
g.println('; pop address of the string chars')
g.code_gen.pop2(Amd64Register.rax) // address of the string
g.println('; push address of the string chars')
g.code_gen.push(Amd64Register.rax)
g.println('; push address of the len:')
g.code_gen.push(Amd64Register.rdx) // len
g.code_gen.mov_var_to_reg(Amd64Register.rdx, LocalVar{i, ast.i64_type_idx, key_var})
g.code_gen.add_reg2(Amd64Register.rax, Amd64Register.rdx)
g.code_gen.mov_deref(Amd64Register.rax, Amd64Register.rax, ast.u8_type_idx)
g.code_gen.mov_reg_to_var(LocalVar{c, ast.u8_type_idx, node.val_var}, Amd64Register.rax) // store the char
end_label := g.labels.new_label()
g.labels.patches << LabelPatch{
id: end_label
pos: jump_addr
}
g.println('; jump to label ${end_label} (end_label)')
start_label := g.labels.new_label() // used for continue
g.labels.branches << BranchLabel{
name: node.label
start: start_label
end: end_label
}
g.stmts(node.stmts) // writes the actual body of the loop
g.labels.addrs[start_label] = g.pos() // used for continue (continue: jump before the inc)
g.println('; label ${start_label} (continue_label)')
g.code_gen.inc_var(LocalVar{i, ast.i64_type_idx, key_var})
g.labels.branches.pop()
g.code_gen.jmp_back(start) // loops
g.labels.addrs[end_label] = g.pos()
g.code_gen.pop2(Amd64Register.rdx) // len
g.code_gen.pop2(Amd64Register.rax) // address of the string
g.println('; label ${end_label} (end_label)')
g.println('; for ${node.val_var} in string }')
/*
} else if node.kind == .array {
} else if node.kind == .array_fixed {
} else if node.kind == .map {
} else if node.kind == .string {
} else if node.kind == .struct {
} else if it.kind in [.array, .string] || it.cond_type.has_flag(.variadic) {
} else if it.kind == .map {

View file

@ -22,7 +22,6 @@ assert !a.is_ascii()
assert '_9'.is_identifier() == true
assert 'a 9'.is_identifier() == false
assert 't'.is_identifier() == true
/*
assert ''.is_blank()
assert ' '.is_blank()
assert ' \t'.is_blank()
@ -48,18 +47,18 @@ assert !a.is_ascii()
assert 'abc'.index_u8(`A`) == -1
assert 'abc'.index_u8(`B`) == -1
assert 'abc'.index_u8(`C`) == -1
/*
assert 'abcabca'.last_index('ca')? == 5
assert 'abcabca'.last_index('ab')? == 3
assert 'abcabca'.last_index('b')? == 4
assert 'Zabcabca'.last_index('Z')? == 0
x := 'Zabcabca'.last_index('Y')
assert x == none
// TODO: `assert 'Zabcabca'.index_last('Y') == none` is a cgen error, 2023/12/04
*/
assert 'abcabca'.last_index_u8(`a`) == 6
assert 'abcabca'.last_index_u8(`c`) == 5
assert 'abcabca'.last_index_u8(`b`) == 4
assert 'Zabcabca'.last_index_u8(`Z`) == 0
//
assert 'abc'.last_index_u8(`d`) == -1
assert 'abc'.last_index_u8(`A`) == -1
assert 'abc'.last_index_u8(`B`) == -1
@ -69,6 +68,7 @@ assert !a.is_ascii()
assert 'abc abca'.contains_u8(`c`)
assert 'abc abca'.contains_u8(` `)
assert !'abc abca'.contains_u8(`A`)
/*
assert 'Abcd'.camel_to_snake() == 'abcd'
assert 'aBcd'.camel_to_snake() == 'a_bcd'
assert 'AAbb'.camel_to_snake() == 'aa_bb'

View file

@ -0,0 +1,21 @@
fn test_for_c_in_string() {
$if windows {
println('0')
println('97')
println('1')
println('98')
println('2')
println('99')
} $else {
s := 'abc'
for i, c in s {
println(i)
println(int(c))
}
}
}
fn main() {
test_for_c_in_string()
}

View file

@ -0,0 +1,6 @@
0
97
1
98
2
99

View file

@ -49,7 +49,7 @@ fn test_native() {
continue
}
}
if test == 'fibonacci_native.vv' {
if test == 'fibonacci_native.vv' || test.contains('linux') {
if user_os == 'windows' {
println('>>> SKIPPING ${test} on windows for now')
continue