Skip to content

Commit c9bec82

Browse files
authored
native: fix unsigned and signed int comparison (#23808)
1 parent c349381 commit c9bec82

4 files changed

Lines changed: 174 additions & 14 deletions

File tree

‎vlib/v/gen/native/amd64.v‎

Lines changed: 153 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -162,30 +162,41 @@ fn (mut c Amd64) neg(reg Amd64Register) {
162162
}
163163

164164
fn (mut c Amd64) cmp(reg Amd64Register, size Size, val i64) {
165+
// for a register 32bits immediate value compare, CMP is REX.W + 81 /7 id
166+
// REX.W -> 0x48 (0x4a here to enable .r8 to .r15)
167+
// 0x81
168+
// modr/m byte:
169+
// /7 -> the reg/opcode bits are 0b111
170+
// the mod bits 0b11 for register value
171+
// R/M bits depends on the register used in the CMP
172+
// see https://www.sandpile.org/x86/opc_rm.htm for a table for modr/m byte (at the bottom of the second one)
173+
165174
if c.g.pref.arch != .amd64 {
166175
panic('cmp')
167176
}
168177
// Second byte depends on the size of the value
169178
match size {
170179
._8 {
171-
c.g.write8(0x48)
172-
c.g.write8(0x83)
180+
c.g.write8(0x48) // REX.W
181+
c.g.write8(0x83) // compares a 64bits register with a 8 bits immediate value
173182
}
174183
._32 {
175-
c.g.write8(0x4a)
176-
c.g.write8(0x81)
184+
c.g.write8(0x4a) // REX.WX
185+
c.g.write8(0x81) // compares a 64bits register with a 32bits immediate value
177186
}
178187
else {
179-
panic('unhandled cmp')
188+
panic('unhandled cmp size ${size}')
180189
}
181190
}
182-
// Third byte depends on the register being compared to
191+
// Third byte (modr/m byte) depends on the regiister being compared to
183192
match reg {
184193
.r12 { c.g.write8(0xfc) }
185194
.rsi { c.g.write8(0x3f) }
186-
.eax { c.g.write8(0xf8) }
195+
.rax { c.g.write8(0xf8) }
196+
.rcx { c.g.write8(0xf9) }
197+
.rdx { c.g.write8(0xfa) }
187198
.rbx { c.g.write8(0xfb) }
188-
else { panic('unhandled cmp') }
199+
else { panic('unhandled cmp reg ${reg}') }
189200
}
190201
match size {
191202
._8 {
@@ -195,7 +206,7 @@ fn (mut c Amd64) cmp(reg Amd64Register, size Size, val i64) {
195206
c.g.write32(i32(val))
196207
}
197208
else {
198-
panic('unhandled cmp')
209+
panic('unhandled cmp size ${size}')
199210
}
200211
}
201212
c.g.println('cmp ${reg}, ${val}')
@@ -232,6 +243,9 @@ fn (mut c Amd64) cmp_reg(reg Amd64Register, reg2 Amd64Register) {
232243
.rax {
233244
c.g.write([u8(0x48), 0x39, 0xc3])
234245
}
246+
.rdx {
247+
c.g.write([u8(0x48), 0x39, 0xd3])
248+
}
235249
else {
236250
c.g.n_error('${@LOCATION} Cannot compare ${reg} and ${reg2}')
237251
}
@@ -2964,6 +2978,7 @@ fn (mut c Amd64) infix_expr(node ast.InfixExpr) {
29642978

29652979
c.g.expr(node.right)
29662980

2981+
right_type := c.g.unwrap(node.right_type)
29672982
left_type := c.g.unwrap(node.left_type)
29682983

29692984
if left_type.is_pure_float() {
@@ -2999,10 +3014,135 @@ fn (mut c Amd64) infix_expr(node ast.InfixExpr) {
29993014
// left: rax, right: rdx
30003015
match node.op {
30013016
.eq, .ne, .gt, .lt, .ge, .le {
3002-
c.cmp_reg(.rax, .rdx)
3003-
// TODO: mov_extend_reg
3004-
c.mov64(Amd64Register.rax, i64(0))
3005-
c.cset_op(node.op)
3017+
if left_type.is_unsigned() && right_type.is_unsigned() {
3018+
c.cmp_reg(.rax, .rdx)
3019+
// TODO: mov_extend_reg
3020+
c.mov64(Amd64Register.rax, i64(0))
3021+
match node.op {
3022+
.gt { c.cset(.a) }
3023+
.lt { c.cset(.b) }
3024+
.ge { c.cset(.ae) }
3025+
.le { c.cset(.be) }
3026+
else { c.cset_op(node.op) }
3027+
}
3028+
} else if left_type.is_unsigned() && right_type.is_signed() {
3029+
c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
3030+
c.mov64(Amd64Register.rax, i64(0))
3031+
match node.op {
3032+
.eq {
3033+
c.cmp(.rdx, ._32, 0)
3034+
c.cset(.ge) // if right >= 0
3035+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3036+
c.cmp_reg(.rbx, .rdx)
3037+
c.cset(.e) // if left (unsigned ==) right
3038+
c.bitand_reg(.rax, .rcx) // only true when right >= 0 and left (unsigned ==) right
3039+
}
3040+
.ne {
3041+
c.cmp(.rdx, ._32, 0)
3042+
c.cset(.l) // if right < 0
3043+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3044+
c.cmp_reg(.rbx, .rdx)
3045+
c.cset(.ne) // if left (unsigned !=) right
3046+
c.bitor_reg(.rax, .rcx) // true when right < 0 or left (unsigned !=) right
3047+
}
3048+
.gt {
3049+
c.cmp(.rdx, ._32, 0)
3050+
c.cset(.l) // if right < 0
3051+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3052+
c.cmp_reg(.rbx, .rdx)
3053+
c.cset(.a) // if left (unsigned >) right
3054+
c.bitor_reg(.rax, .rcx) // true when right < 0 or left (unsigned >) right
3055+
}
3056+
.lt {
3057+
c.cmp(.rdx, ._32, 0)
3058+
c.cset(.ge) // if right >= 0
3059+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3060+
c.cmp_reg(.rbx, .rdx)
3061+
c.cset(.b) // if left (unsigned >) right
3062+
c.bitand_reg(.rax, .rcx) // true when right >= 0 and left (unsigned <) right
3063+
}
3064+
.ge {
3065+
c.cmp(.rdx, ._32, 0)
3066+
c.cset(.l) // if right < 0
3067+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3068+
c.cmp_reg(.rbx, .rdx)
3069+
c.cset(.ae) // if left (unsigned >=) right
3070+
c.bitor_reg(.rax, .rcx) // true when right < 0 or left (unsigned >=) right
3071+
}
3072+
.le {
3073+
c.cmp(.rdx, ._32, 0)
3074+
c.cset(.ge) // if right >= 0
3075+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3076+
c.cmp_reg(.rbx, .rdx)
3077+
c.cset(.be) // if left (unsigned <=) right
3078+
c.bitand_reg(.rax, .rcx) // true when right >= 0 and left (unsigned <=) right
3079+
}
3080+
else {
3081+
c.g.n_error('${@LOCATION} unhandled op ${node.op}')
3082+
}
3083+
}
3084+
} else if left_type.is_signed() && right_type.is_unsigned() {
3085+
c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
3086+
c.mov64(Amd64Register.rax, i64(0))
3087+
match node.op {
3088+
.eq {
3089+
c.cmp(.rbx, ._32, 0)
3090+
c.cset(.ge) // if left >= 0
3091+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3092+
c.cmp_reg(.rbx, .rdx)
3093+
c.cset(.e) // if left (unsigned ==) right
3094+
c.bitand_reg(.rax, .rcx) // only true when left >= 0 and left (unsigned ==) right
3095+
}
3096+
.ne {
3097+
c.cmp(.rbx, ._32, 0)
3098+
c.cset(.l) // if left < 0
3099+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3100+
c.cmp_reg(.rbx, .rdx)
3101+
c.cset(.ne) // if left (unsigned !=) right
3102+
c.bitor_reg(.rax, .rcx) // true when left < 0 or left (unsigned !=) right
3103+
}
3104+
.gt {
3105+
c.cmp(.rbx, ._32, 0)
3106+
c.cset(.ge) // if left >= 0
3107+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3108+
c.cmp_reg(.rbx, .rdx)
3109+
c.cset(.a) // if left (unsigned >) right
3110+
c.bitand_reg(.rax, .rcx) // true when left >= 0 and left (unsigned >) right
3111+
}
3112+
.lt {
3113+
c.cmp(.rbx, ._32, 0)
3114+
c.cset(.l) // if left < 0
3115+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3116+
c.cmp_reg(.rbx, .rdx)
3117+
c.cset(.b) // if left (unsigned >) right
3118+
c.bitor_reg(.rax, .rcx) // true when left < 0 or left (unsigned <) right
3119+
}
3120+
.ge {
3121+
c.cmp(.rbx, ._32, 0)
3122+
c.cset(.ge) // if left >= 0
3123+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3124+
c.cmp_reg(.rbx, .rdx)
3125+
c.cset(.ae) // if left (unsigned >=) right
3126+
c.bitand_reg(.rax, .rcx) // true when left >= 0 and left (unsigned >=) right
3127+
}
3128+
.le {
3129+
c.cmp(.rbx, ._32, 0)
3130+
c.cset(.l) // if left < 0
3131+
c.mov_reg(Amd64Register.rcx, Amd64Register.rax)
3132+
c.cmp_reg(.rbx, .rdx)
3133+
c.cset(.be) // if left (unsigned <=) right
3134+
c.bitor_reg(.rax, .rcx) // true when left < 0 or left (unsigned <=) right
3135+
}
3136+
else {
3137+
c.g.n_error('${@LOCATION} unhandled op ${node.op}')
3138+
}
3139+
}
3140+
} else {
3141+
c.cmp_reg(.rax, .rdx)
3142+
// TODO: mov_extend_reg
3143+
c.mov64(Amd64Register.rax, i64(0))
3144+
c.cset_op(node.op)
3145+
}
30063146
}
30073147
.plus {
30083148
c.add_reg(.rax, .rdx)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,9 @@ fn (mut g Gen) gen_flag_hash_stmt(node ast.HashStmt) {
328328
} else if node.main.contains('-L') {
329329
g.linker_include_paths << node.main.all_after('-L').trim_space()
330330
} else if node.main.contains('-D') || node.main.contains('-I') {
331-
g.v_error('`-D` and `-I` flags are not supported with the native backend', node.pos)
331+
// g.v_error('`-D` and `-I` flags are not supported with the native backend', node.pos)
332+
println(util.formatted_error('warn', '`-D` and `-I` flags are not supported with the native backend',
333+
g.current_file.path, node.pos))
332334
} else {
333335
g.v_error('unknown `#flag` format: `${node.main}`', node.pos)
334336
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// taken from vlib/v/tests/int_cmp_test.v
2+
3+
assert i8(3) > i16(-10)
4+
assert i16(-9) > int(-11)
5+
assert i64(-12) <= i8(-12)
6+
assert i64(-43232554) < i8(-126)
7+
8+
assert u8(3) < u16(10)
9+
assert u16(40000) > u32(200)
10+
assert u64(18161419857654944321) >= u8(12)
11+
assert u64(40000) < u16(40001)
12+
13+
assert u8(12) > i8(-12)
14+
assert i16(-27) < u32(65463356)
15+
assert u32(8543) > int(-7523)
16+
assert i64(-89) <= u64(567)
17+
assert int(-1) != u32(0xffffffff)
18+
assert !(u64(0xfffffffffffffffe) == i64(-2))

‎vlib/v/gen/native/tests/vtest_int_cmp.vv.out‎

Whitespace-only changes.

0 commit comments

Comments
 (0)