Compare commits

...

2 commits

Author SHA1 Message Date
Delyan Angelov
d73ebc1aca
v.ast: add a mod field to ast.FnTypeDecl too
Some checks are pending
sdl CI / v-compiles-sdl-examples (push) Waiting to run
Time CI / time-linux (push) Waiting to run
Graphics CI / gg-regressions (push) Waiting to run
vlib modules CI / build-module-docs (push) Waiting to run
native backend CI / native-backend-ubuntu (push) Waiting to run
native backend CI / native-backend-windows (push) Waiting to run
Sanitized CI / sanitize-undefined-clang (push) Waiting to run
Sanitized CI / sanitize-undefined-gcc (push) Waiting to run
Sanitized CI / tests-sanitize-address-clang (push) Waiting to run
Sanitized CI / sanitize-address-msvc (push) Waiting to run
Sanitized CI / sanitize-address-gcc (push) Waiting to run
Sanitized CI / sanitize-memory-clang (push) Waiting to run
Time CI / time-macos (push) Waiting to run
Time CI / time-windows (push) Waiting to run
toml CI / toml-module-pass-external-test-suites (push) Waiting to run
Tools CI / tools-linux (clang) (push) Waiting to run
Tools CI / tools-linux (gcc) (push) Waiting to run
Tools CI / tools-linux (tcc) (push) Waiting to run
Tools CI / tools-macos (clang) (push) Waiting to run
Tools CI / tools-windows (gcc) (push) Waiting to run
Tools CI / tools-windows (msvc) (push) Waiting to run
Tools CI / tools-windows (tcc) (push) Waiting to run
Tools CI / tools-docker-ubuntu-musl (push) Waiting to run
vab CI / vab-compiles-v-examples (push) Waiting to run
vab CI / v-compiles-os-android (push) Waiting to run
wasm backend CI / wasm-backend (ubuntu-22.04) (push) Waiting to run
wasm backend CI / wasm-backend (windows-2022) (push) Waiting to run
2025-09-11 16:15:33 +03:00
Eliyaan (Nopana)
fd479a2bcd
native: support more assign ops, reduce code duplication (#25283) 2025-09-11 16:12:28 +03:00
5 changed files with 194 additions and 153 deletions

View file

@ -897,6 +897,7 @@ fn (t Tree) fn_type_decl(node ast.FnTypeDecl) &Node {
mut obj := create_object() mut obj := create_object()
obj.add_terse('ast_type', t.string_node('FnTypeDecl')) obj.add_terse('ast_type', t.string_node('FnTypeDecl'))
obj.add_terse('name', t.string_node(node.name)) obj.add_terse('name', t.string_node(node.name))
obj.add_terse('mod', t.string_node(node.mod))
obj.add_terse('is_pub', t.bool_node(node.is_pub)) obj.add_terse('is_pub', t.bool_node(node.is_pub))
obj.add_terse('typ', t.type_node(node.typ)) obj.add_terse('typ', t.type_node(node.typ))
obj.add('pos', t.pos(node.pos)) obj.add('pos', t.pos(node.pos))

View file

@ -1522,6 +1522,7 @@ pub mut:
pub struct FnTypeDecl { pub struct FnTypeDecl {
pub: pub:
name string name string
mod string
is_pub bool is_pub bool
typ Type typ Type
pos token.Pos pos token.Pos

View file

@ -1753,10 +1753,8 @@ fn (mut c Amd64) mov(r Register, val i32) {
} }
} }
fn (mut c Amd64) mul_reg(a Amd64Register, b Amd64Register) { // rax times b
if a != .rax { fn (mut c Amd64) mul_reg_rax(b Amd64Register) {
c.g.n_error('mul always operates on rax')
}
match b { match b {
.rax { .rax {
c.g.write8(0x48) c.g.write8(0x48)
@ -1799,10 +1797,8 @@ fn (mut c Amd64) imul_reg(r Amd64Register) {
} }
} }
fn (mut c Amd64) div_reg(a Amd64Register, b Amd64Register) { // rax divided by b
if a != .rax { fn (mut c Amd64) div_reg_rax(b Amd64Register) {
c.g.n_error('div always operates on rax')
}
match b { match b {
.rax { .rax {
c.g.write8(0x48) c.g.write8(0x48)
@ -1831,8 +1827,9 @@ fn (mut c Amd64) div_reg(a Amd64Register, b Amd64Register) {
c.g.println('div ${b}') c.g.println('div ${b}')
} }
fn (mut c Amd64) mod_reg(a Amd64Register, b Amd64Register) { // rax % b
c.div_reg(a, b) fn (mut c Amd64) mod_reg_rax(b Amd64Register) {
c.div_reg_rax(b)
c.mov_reg(Amd64Register.rdx, Amd64Register.rax) c.mov_reg(Amd64Register.rdx, Amd64Register.rax)
} }
@ -2287,36 +2284,6 @@ fn (mut c Amd64) assign_var(var IdentVar, raw_type ast.Type) {
// TODO: may have a problem if the literal is bigger than max_i64: needs u64 // TODO: may have a problem if the literal is bigger than max_i64: needs u64
fn (mut c Amd64) assign_ident_int_lit(node ast.AssignStmt, i i32, int_lit ast.IntegerLiteral, left ast.Ident) { fn (mut c Amd64) assign_ident_int_lit(node ast.AssignStmt, i i32, int_lit ast.IntegerLiteral, left ast.Ident) {
match node.op { match node.op {
.plus_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
c.add_reg(Amd64Register.rax, Amd64Register.rdx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.minus_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
c.sub_reg(Amd64Register.rax, Amd64Register.rdx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.mult_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
c.mul_reg(.rax, .rdx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.div_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
c.div_reg(.rax, .rdx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.mod_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
c.mod_reg(.rax, .rdx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.decl_assign { .decl_assign {
c.allocate_var(left.name, 8, i64(int_lit.val.int())) c.allocate_var(left.name, 8, i64(int_lit.val.int()))
} }
@ -2324,56 +2291,23 @@ fn (mut c Amd64) assign_ident_int_lit(node ast.AssignStmt, i i32, int_lit ast.In
c.mov64(Amd64Register.rax, i64(int_lit.val.int())) c.mov64(Amd64Register.rax, i64(int_lit.val.int()))
c.mov_reg_to_var(left, Amd64Register.rax) c.mov_reg_to_var(left, Amd64Register.rax)
} }
.left_shift_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
c.shl_reg(.rax, .rcx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.right_shift_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
c.sar_reg(.rax, .rcx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.unsigned_right_shift_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
c.shr_reg(.rax, .rcx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.xor_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
c.bitxor_reg(.rax, .rcx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.or_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
c.bitor_reg(.rax, .rcx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.and_assign {
c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
c.bitand_reg(.rax, .rcx)
c.mov_reg_to_var(left, Amd64Register.rax)
}
.boolean_and_assign { .boolean_and_assign {
c.mov_var_to_reg(Amd64Register.rax, left) c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int())) c.mov64(Amd64Register.rbx, i64(int_lit.val.int()))
c.bitand_reg(.rax, .rcx) c.bitand_reg(.rax, .rbx)
c.mov_reg_to_var(left, Amd64Register.rax) c.mov_reg_to_var(left, Amd64Register.rax)
} }
.boolean_or_assign { .boolean_or_assign {
c.mov_var_to_reg(Amd64Register.rax, left) c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int())) c.mov64(Amd64Register.rbx, i64(int_lit.val.int()))
c.bitor_reg(.rax, .rcx) c.bitor_reg(.rax, .rbx)
c.mov_reg_to_var(left, Amd64Register.rax) c.mov_reg_to_var(left, Amd64Register.rax)
} }
else { else {
c.g.n_error('${@LOCATION} unexpected assignment op ${node.op}') c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rbx, i64(int_lit.val.int()))
c.apply_op_int(.rax, .rbx, node.op)
c.mov_reg_to_var(left, Amd64Register.rax)
} }
} }
} }
@ -2591,19 +2525,26 @@ fn (mut c Amd64) assign_ident_right_expr(node ast.AssignStmt, i i32, right ast.E
c.mov_ssereg_to_var(ident, .xmm1) c.mov_ssereg_to_var(ident, .xmm1)
} else if left_type.is_int() { } else if left_type.is_int() {
c.mov_var_to_reg(Amd64Register.rbx, ident) c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
c.mov_var_to_reg(Amd64Register.rax, ident)
match node.op { c.apply_op_int(.rax, .rbx, node.op)
.plus_assign { c.add_reg(.rbx, .rax) } c.mov_reg_to_var(ident, Amd64Register.rax)
.minus_assign { c.sub_reg(.rbx, .rax) }
.div_assign { c.div_reg(.rbx, .rax) }
.mult_assign { c.mul_reg(.rbx, .rax) }
else { c.g.n_error('${@LOCATION} unexpected assignment operator ${node.op} for i32') }
}
c.mov_reg_to_var(ident, Amd64Register.rbx)
} else { } else {
c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}') match node.op {
.boolean_and_assign {
c.mov_var_to_reg(Amd64Register.rbx, ident)
c.bitand_reg(.rbx, .rax)
c.mov_reg_to_var(ident, Amd64Register.rbx)
}
.boolean_or_assign {
c.mov_var_to_reg(Amd64Register.rbx, ident)
c.bitor_reg(.rbx, .rax)
c.mov_reg_to_var(ident, Amd64Register.rbx)
}
else {
c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}')
}
}
} }
} }
} }
@ -2631,19 +2572,26 @@ fn (mut c Amd64) assign_ident_right_expr(node ast.AssignStmt, i i32, right ast.E
c.mov_ssereg_to_var(ident, .xmm1) c.mov_ssereg_to_var(ident, .xmm1)
} else if left_type.is_int() { } else if left_type.is_int() {
c.mov_var_to_reg(Amd64Register.rbx, ident) c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
c.mov_var_to_reg(Amd64Register.rax, ident)
match node.op { c.apply_op_int(.rax, .rbx, node.op)
.plus_assign { c.add_reg(.rbx, .rax) } c.mov_reg_to_var(ident, Amd64Register.rax)
.minus_assign { c.sub_reg(.rbx, .rax) }
.div_assign { c.div_reg(.rbx, .rax) }
.mult_assign { c.mul_reg(.rbx, .rax) }
else { c.g.n_error('${@LOCATION} unexpected assignment operator ${node.op} for i32') }
}
c.mov_reg_to_var(ident, Amd64Register.rbx)
} else { } else {
c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}') match node.op {
.boolean_and_assign {
c.mov_var_to_reg(Amd64Register.rbx, ident)
c.bitand_reg(.rbx, .rax)
c.mov_reg_to_var(ident, Amd64Register.rbx)
}
.boolean_or_assign {
c.mov_var_to_reg(Amd64Register.rbx, ident)
c.bitor_reg(.rbx, .rax)
c.mov_reg_to_var(ident, Amd64Register.rbx)
}
else {
c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}')
}
}
} }
} }
} }
@ -2680,6 +2628,59 @@ fn (mut c Amd64) assign_ident_right_expr(node ast.AssignStmt, i i32, right ast.E
}*/ }*/
} }
// /!\ for div, mul, mod the left value should always be .rax
fn (mut c Amd64) apply_op_int(left_value Amd64Register, right_value Amd64Register, op token.Kind) {
match op {
.plus_assign {
c.add_reg(left_value, right_value)
}
.minus_assign {
c.sub_reg(left_value, right_value)
}
.div_assign {
if left_value != .rax {
c.g.n_error('@{LOCATION} div always operates on rax')
}
c.mov(Amd64Register.rdx, i32(0)) // 64bits IDIV uses RDX:RAX
c.div_reg_rax(right_value)
}
.mult_assign {
if left_value != .rax {
c.g.n_error('@{LOCATION} mul always operates on rax')
}
c.mul_reg_rax(right_value)
}
.xor_assign {
c.bitxor_reg(left_value, right_value)
}
.mod_assign {
if left_value != .rax {
c.g.n_error('@{LOCATION} mod always operates on rax')
}
c.mov(Amd64Register.rdx, i32(0)) // 64bits IDIV uses RDX:RAX
c.mod_reg_rax(right_value)
}
.or_assign {
c.bitor_reg(left_value, right_value)
}
.and_assign {
c.bitand_reg(left_value, right_value)
}
.right_shift_assign {
c.shr_reg(left_value, right_value)
}
.left_shift_assign {
c.shl_reg(left_value, right_value)
}
.unsigned_right_shift_assign {
c.sar_reg(left_value, right_value)
}
else {
c.g.n_error('${@LOCATION} unexpected operator ${op} for int')
}
}
}
fn (mut c Amd64) gen_type_promotion(from ast.Type, to ast.Type, option Amd64RegisterOption) { fn (mut c Amd64) gen_type_promotion(from ast.Type, to ast.Type, option Amd64RegisterOption) {
if !to.is_pure_float() { if !to.is_pure_float() {
return return
@ -3077,52 +3078,6 @@ fn (mut c Amd64) assign_stmt(node ast.AssignStmt) {
.decl_assign, .assign { .decl_assign, .assign {
c.mov_store(.rbx, .rcx, size) c.mov_store(.rbx, .rcx, size)
} }
.plus_assign {
c.add_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.minus_assign {
c.sub_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.and_assign {
c.bitand_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.mod_assign {
c.mov(Amd64Register.rdx, i32(0)) // 64bits IDIV uses RDX:RAX
c.mod_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.mult_assign {
c.mul_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.div_assign {
c.mov(Amd64Register.rdx, i32(0)) // 64bits IDIV uses RDX:RAX
c.div_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.xor_assign {
c.bitxor_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.or_assign {
c.bitor_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.right_shift_assign {
c.shr_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.left_shift_assign {
c.shl_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.unsigned_right_shift_assign {
c.sar_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size)
}
.boolean_and_assign { .boolean_and_assign {
c.bitand_reg(.rax, .rcx) c.bitand_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size) c.mov_store(.rbx, .rax, size)
@ -3132,7 +3087,8 @@ fn (mut c Amd64) assign_stmt(node ast.AssignStmt) {
c.mov_store(.rbx, .rax, size) c.mov_store(.rbx, .rax, size)
} }
else { else {
c.g.n_error('${@LOCATION} Unsupported assign instruction (${node.op})') c.apply_op_int(.rax, .rcx, node.op)
c.mov_store(.rbx, .rax, size)
} }
} }
} else if var_type.is_pure_float() { } else if var_type.is_pure_float() {
@ -4289,7 +4245,7 @@ fn (mut c Amd64) convert_int_to_string(a Register, b Register) {
c.mov(Amd64Register.rdx, 0) // upperhalf of the dividend c.mov(Amd64Register.rdx, 0) // upperhalf of the dividend
c.mov(Amd64Register.rbx, 10) c.mov(Amd64Register.rbx, 10)
c.div_reg(.rax, .rbx) // rax will be the result of the division 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.add8(.rdx, i32(`0`)) // rdx is the remainder, add 48 to convert it into it's ascii representation
c.mov_store(.rdi, .rdx, ._8) c.mov_store(.rdi, .rdx, ._8)

View file

@ -7,6 +7,88 @@ fn main() {
test_alias(100, 9) test_alias(100, 9)
test_plus_assign() test_plus_assign()
test_minus_assign() test_minus_assign()
op_assigns_test()
if_expr_op_assigns_test()
}
fn if_expr_op_assigns_test() {
mut b := 1
one := 1
two := 2
three := 3
four := 4
five := 5
ten := 10
b += if false {0} else {one}
b -= if false {0} else {one}
assert b == 1
b |= if false {0} else {two}
assert b == 3
b &= if false {0} else {one}
assert b == 1
b ^= if false {0} else {five}
assert b == 4
b %= if false {0} else {three}
assert b == 1
b *= if false {0} else {ten}
b /= if false {0} else {ten}
assert b == 1
b <<= if false {0} else {four}
b >>>= if false {0} else {two}
b >>= if false {0} else {two}
assert b == 1
mut var := true
t := true
f := false
var &&= if false {f} else {t}
assert var == true
var &&= if false {t} else {f}
assert var == false
var ||= if false {f} else {t}
assert var == true
var ||= if false {t} else {f}
assert var == true
}
fn op_assigns_test() {
mut b := 1
one := 1
two := 2
three := 3
four := 4
five := 5
ten := 10
b += one
b -= one
assert b == 1
b |= two
assert b == 3
b &= one
assert b == 1
b ^= five
assert b == 4
b %= three
assert b == 1
b *= ten
b /= ten
assert b == 1
b <<= four
b >>>= two
b >>= two
assert b == 1
mut var := true
t := true
f := false
var &&= t
assert var == true
var &&= f
assert var == false
var ||= t
assert var == true
var ||= f
assert var == true
} }
fn test_minus_assign() { fn test_minus_assign() {

View file

@ -2782,6 +2782,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
p.attrs = [] p.attrs = []
return ast.FnTypeDecl{ return ast.FnTypeDecl{
name: fn_name name: fn_name
mod: p.mod
is_pub: is_pub is_pub: is_pub
typ: fn_type typ: fn_type
pos: decl_pos pos: decl_pos