native: fix unsigned and signed int comparison (#23808)

This commit is contained in:
Eliyaan (Nopana) 2025-02-26 02:52:37 +01:00 committed by GitHub
parent c349381aa5
commit c9bec82033
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 174 additions and 14 deletions

View file

@ -162,30 +162,41 @@ fn (mut c Amd64) neg(reg Amd64Register) {
} }
fn (mut c Amd64) cmp(reg Amd64Register, size Size, val i64) { fn (mut c Amd64) cmp(reg Amd64Register, size Size, val i64) {
// for a register 32bits immediate value compare, CMP is REX.W + 81 /7 id
// REX.W -> 0x48 (0x4a here to enable .r8 to .r15)
// 0x81
// modr/m byte:
// /7 -> the reg/opcode bits are 0b111
// the mod bits 0b11 for register value
// R/M bits depends on the register used in the CMP
// see https://www.sandpile.org/x86/opc_rm.htm for a table for modr/m byte (at the bottom of the second one)
if c.g.pref.arch != .amd64 { if c.g.pref.arch != .amd64 {
panic('cmp') panic('cmp')
} }
// Second byte depends on the size of the value // Second byte depends on the size of the value
match size { match size {
._8 { ._8 {
c.g.write8(0x48) c.g.write8(0x48) // REX.W
c.g.write8(0x83) c.g.write8(0x83) // compares a 64bits register with a 8 bits immediate value
} }
._32 { ._32 {
c.g.write8(0x4a) c.g.write8(0x4a) // REX.WX
c.g.write8(0x81) c.g.write8(0x81) // compares a 64bits register with a 32bits immediate value
} }
else { else {
panic('unhandled cmp') panic('unhandled cmp size ${size}')
} }
} }
// Third byte depends on the register being compared to // Third byte (modr/m byte) depends on the regiister being compared to
match reg { match reg {
.r12 { c.g.write8(0xfc) } .r12 { c.g.write8(0xfc) }
.rsi { c.g.write8(0x3f) } .rsi { c.g.write8(0x3f) }
.eax { c.g.write8(0xf8) } .rax { c.g.write8(0xf8) }
.rcx { c.g.write8(0xf9) }
.rdx { c.g.write8(0xfa) }
.rbx { c.g.write8(0xfb) } .rbx { c.g.write8(0xfb) }
else { panic('unhandled cmp') } else { panic('unhandled cmp reg ${reg}') }
} }
match size { match size {
._8 { ._8 {
@ -195,7 +206,7 @@ fn (mut c Amd64) cmp(reg Amd64Register, size Size, val i64) {
c.g.write32(i32(val)) c.g.write32(i32(val))
} }
else { else {
panic('unhandled cmp') panic('unhandled cmp size ${size}')
} }
} }
c.g.println('cmp ${reg}, ${val}') c.g.println('cmp ${reg}, ${val}')
@ -232,6 +243,9 @@ fn (mut c Amd64) cmp_reg(reg Amd64Register, reg2 Amd64Register) {
.rax { .rax {
c.g.write([u8(0x48), 0x39, 0xc3]) c.g.write([u8(0x48), 0x39, 0xc3])
} }
.rdx {
c.g.write([u8(0x48), 0x39, 0xd3])
}
else { else {
c.g.n_error('${@LOCATION} Cannot compare ${reg} and ${reg2}') c.g.n_error('${@LOCATION} Cannot compare ${reg} and ${reg2}')
} }
@ -2964,6 +2978,7 @@ fn (mut c Amd64) infix_expr(node ast.InfixExpr) {
c.g.expr(node.right) c.g.expr(node.right)
right_type := c.g.unwrap(node.right_type)
left_type := c.g.unwrap(node.left_type) left_type := c.g.unwrap(node.left_type)
if left_type.is_pure_float() { if left_type.is_pure_float() {
@ -2999,10 +3014,135 @@ fn (mut c Amd64) infix_expr(node ast.InfixExpr) {
// left: rax, right: rdx // left: rax, right: rdx
match node.op { match node.op {
.eq, .ne, .gt, .lt, .ge, .le { .eq, .ne, .gt, .lt, .ge, .le {
c.cmp_reg(.rax, .rdx) if left_type.is_unsigned() && right_type.is_unsigned() {
// TODO: mov_extend_reg c.cmp_reg(.rax, .rdx)
c.mov64(Amd64Register.rax, i64(0)) // TODO: mov_extend_reg
c.cset_op(node.op) c.mov64(Amd64Register.rax, i64(0))
match node.op {
.gt { c.cset(.a) }
.lt { c.cset(.b) }
.ge { c.cset(.ae) }
.le { c.cset(.be) }
else { c.cset_op(node.op) }
}
} else if left_type.is_unsigned() && right_type.is_signed() {
c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
c.mov64(Amd64Register.rax, i64(0))
match node.op {
.eq {
c.cmp(.rdx, ._32, 0)
c.cset(.ge) // if right >= 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.e) // if left (unsigned ==) right
c.bitand_reg(.rax, .rcx) // only true when right >= 0 and left (unsigned ==) right
}
.ne {
c.cmp(.rdx, ._32, 0)
c.cset(.l) // if right < 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.ne) // if left (unsigned !=) right
c.bitor_reg(.rax, .rcx) // true when right < 0 or left (unsigned !=) right
}
.gt {
c.cmp(.rdx, ._32, 0)
c.cset(.l) // if right < 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.a) // if left (unsigned >) right
c.bitor_reg(.rax, .rcx) // true when right < 0 or left (unsigned >) right
}
.lt {
c.cmp(.rdx, ._32, 0)
c.cset(.ge) // if right >= 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.b) // if left (unsigned >) right
c.bitand_reg(.rax, .rcx) // true when right >= 0 and left (unsigned <) right
}
.ge {
c.cmp(.rdx, ._32, 0)
c.cset(.l) // if right < 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.ae) // if left (unsigned >=) right
c.bitor_reg(.rax, .rcx) // true when right < 0 or left (unsigned >=) right
}
.le {
c.cmp(.rdx, ._32, 0)
c.cset(.ge) // if right >= 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.be) // if left (unsigned <=) right
c.bitand_reg(.rax, .rcx) // true when right >= 0 and left (unsigned <=) right
}
else {
c.g.n_error('${@LOCATION} unhandled op ${node.op}')
}
}
} else if left_type.is_signed() && right_type.is_unsigned() {
c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
c.mov64(Amd64Register.rax, i64(0))
match node.op {
.eq {
c.cmp(.rbx, ._32, 0)
c.cset(.ge) // if left >= 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.e) // if left (unsigned ==) right
c.bitand_reg(.rax, .rcx) // only true when left >= 0 and left (unsigned ==) right
}
.ne {
c.cmp(.rbx, ._32, 0)
c.cset(.l) // if left < 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.ne) // if left (unsigned !=) right
c.bitor_reg(.rax, .rcx) // true when left < 0 or left (unsigned !=) right
}
.gt {
c.cmp(.rbx, ._32, 0)
c.cset(.ge) // if left >= 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.a) // if left (unsigned >) right
c.bitand_reg(.rax, .rcx) // true when left >= 0 and left (unsigned >) right
}
.lt {
c.cmp(.rbx, ._32, 0)
c.cset(.l) // if left < 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.b) // if left (unsigned >) right
c.bitor_reg(.rax, .rcx) // true when left < 0 or left (unsigned <) right
}
.ge {
c.cmp(.rbx, ._32, 0)
c.cset(.ge) // if left >= 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.ae) // if left (unsigned >=) right
c.bitand_reg(.rax, .rcx) // true when left >= 0 and left (unsigned >=) right
}
.le {
c.cmp(.rbx, ._32, 0)
c.cset(.l) // if left < 0
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
c.cmp_reg(.rbx, .rdx)
c.cset(.be) // if left (unsigned <=) right
c.bitor_reg(.rax, .rcx) // true when left < 0 or left (unsigned <=) right
}
else {
c.g.n_error('${@LOCATION} unhandled op ${node.op}')
}
}
} else {
c.cmp_reg(.rax, .rdx)
// TODO: mov_extend_reg
c.mov64(Amd64Register.rax, i64(0))
c.cset_op(node.op)
}
} }
.plus { .plus {
c.add_reg(.rax, .rdx) c.add_reg(.rax, .rdx)

View file

@ -328,7 +328,9 @@ fn (mut g Gen) gen_flag_hash_stmt(node ast.HashStmt) {
} else if node.main.contains('-L') { } else if node.main.contains('-L') {
g.linker_include_paths << node.main.all_after('-L').trim_space() g.linker_include_paths << node.main.all_after('-L').trim_space()
} else if node.main.contains('-D') || node.main.contains('-I') { } else if node.main.contains('-D') || node.main.contains('-I') {
g.v_error('`-D` and `-I` flags are not supported with the native backend', node.pos) // g.v_error('`-D` and `-I` flags are not supported with the native backend', node.pos)
println(util.formatted_error('warn', '`-D` and `-I` flags are not supported with the native backend',
g.current_file.path, node.pos))
} else { } else {
g.v_error('unknown `#flag` format: `${node.main}`', node.pos) g.v_error('unknown `#flag` format: `${node.main}`', node.pos)
} }

View file

@ -0,0 +1,18 @@
// taken from vlib/v/tests/int_cmp_test.v
assert i8(3) > i16(-10)
assert i16(-9) > int(-11)
assert i64(-12) <= i8(-12)
assert i64(-43232554) < i8(-126)
assert u8(3) < u16(10)
assert u16(40000) > u32(200)
assert u64(18161419857654944321) >= u8(12)
assert u64(40000) < u16(40001)
assert u8(12) > i8(-12)
assert i16(-27) < u32(65463356)
assert u32(8543) > int(-7523)
assert i64(-89) <= u64(567)
assert int(-1) != u32(0xffffffff)
assert !(u64(0xfffffffffffffffe) == i64(-2))