Skip to content

Commit 7fc98eb

Browse files
bmourad01bmourad01
andauthored
Reimplements x86 bitscan and popcnt (#1416)
* Update LLVM backend to work with version 12 This may also work with later versions, but I did not test them * Reimplements bitscan in the x86 BIL lifter * Fixes the algorithm and extends to `lzcnt/tzcnt` * Reverts to previous public interface We can detect the f3 prefix from the lifter itself * Switches to branchless implementation Also gives us better popcnt for free * Fix the shift value for popcnt * Forgot to assign the source operand to temporary * Only XOR with mask for BSR Co-authored-by: bmourad01 <[email protected]>
1 parent 34e5f97 commit 7fc98eb

File tree

1 file changed

+137
-30
lines changed

1 file changed

+137
-30
lines changed

plugins/x86/x86_lifter.ml

Lines changed: 137 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ module ToIR = struct
189189
else
190190
Bil.Move (v, Bil.(Var v + (df_to_offset mode df_e * i (bytes_of_width t))))
191191

192-
let rep_wrap ?check_zf ~mode ~addr ~next stmts =
192+
let rep_wrap ?check_zf ~mode ~addr:_ ~next:_ stmts =
193193
let extend = big_int_of_mode mode in
194194
let zero = extend Word.b0 and one = extend Word.b1 in
195195
let rcx = match mode with
@@ -273,6 +273,95 @@ module ToIR = struct
273273
Bil.Move (cf, Bil.(s2 > s1))
274274
::set_aopszf_sub t s1 s2 r
275275

276+
let popcount x ~one ~two ~four ~sh ~m1 ~m2 ~m4 ~h01 = Bil.[
277+
x := var x - ((var x lsr int one) land int m1);
278+
x := (var x land int m2) + ((var x lsr int two) land int m2);
279+
x := (var x + (var x lsr int four)) land int m4;
280+
x := (var x * int h01) lsr int sh;
281+
]
282+
283+
let popcnt16 x =
284+
let one = Word.one 16 in
285+
let two = Word.succ one in
286+
let four = Word.of_int ~width:16 4 in
287+
let sh = Word.of_int ~width:16 8 in
288+
let m1 = Word.of_int ~width:16 0x5555 in
289+
let m2 = Word.of_int ~width:16 0x3333 in
290+
let m4 = Word.of_int ~width:16 0x0f0f in
291+
let h01 = Word.of_int ~width:16 0x0101 in
292+
popcount x ~one ~two ~four ~sh ~m1 ~m2 ~m4 ~h01
293+
294+
let popcnt32 x =
295+
let one = Word.one 32 in
296+
let two = Word.succ one in
297+
let four = Word.of_int ~width:32 4 in
298+
let sh = Word.of_int ~width:32 24 in
299+
let m1 = Word.of_int ~width:32 0x55555555 in
300+
let m2 = Word.of_int ~width:32 0x33333333 in
301+
let m4 = Word.of_int ~width:32 0x0f0f0f0f in
302+
let h01 = Word.of_int ~width:32 0x01010101 in
303+
popcount x ~one ~two ~four ~sh ~m1 ~m2 ~m4 ~h01
304+
305+
let popcnt64 x =
306+
let one = Word.one 64 in
307+
let two = Word.succ one in
308+
let four = Word.of_int ~width:64 4 in
309+
let sh = Word.of_int ~width:64 56 in
310+
let m1 = Word.of_int64 ~width:64 0x5555555555555555L in
311+
let m2 = Word.of_int64 ~width:64 0x3333333333333333L in
312+
let m4 = Word.of_int64 ~width:64 0x0f0f0f0f0f0f0f0fL in
313+
let h01 = Word.of_int64 ~width:64 0x0101010101010101L in
314+
popcount x ~one ~two ~four ~sh ~m1 ~m2 ~m4 ~h01
315+
316+
let clz16 x = Bil.[
317+
x := var x lor (var x lsr int Word.(one 16));
318+
x := var x lor (var x lsr int Word.(of_int 2 ~width:16));
319+
x := var x lor (var x lsr int Word.(of_int 4 ~width:16));
320+
x := var x lor (var x lsr int Word.(of_int 8 ~width:16));
321+
x := lnot (var x);
322+
] @ popcnt16 x
323+
324+
let clz32 x = Bil.[
325+
x := var x lor (var x lsr int Word.(one 32));
326+
x := var x lor (var x lsr int Word.(of_int 2 ~width:32));
327+
x := var x lor (var x lsr int Word.(of_int 4 ~width:32));
328+
x := var x lor (var x lsr int Word.(of_int 8 ~width:32));
329+
x := var x lor (var x lsr int Word.(of_int 16 ~width:32));
330+
x := lnot (var x);
331+
] @ popcnt32 x
332+
333+
let clz64 x = Bil.[
334+
x := var x lor (var x lsr int Word.(one 64));
335+
x := var x lor (var x lsr int Word.(of_int 2 ~width:64));
336+
x := var x lor (var x lsr int Word.(of_int 4 ~width:64));
337+
x := var x lor (var x lsr int Word.(of_int 8 ~width:64));
338+
x := var x lor (var x lsr int Word.(of_int 16 ~width:64));
339+
x := var x lor (var x lsr int Word.(of_int 32 ~width:64));
340+
x := lnot (var x);
341+
] @ popcnt64 x
342+
343+
let ctz16 x = Bil.(
344+
x := (var x land unop NEG (var x)) - int Word.(one 16)
345+
) :: popcnt16 x
346+
347+
let ctz32 x = Bil.(
348+
x := (var x land unop NEG (var x)) - int Word.(one 32)
349+
) :: popcnt32 x
350+
351+
let ctz64 x = Bil.(
352+
x := (var x land unop NEG (var x)) - int Word.(one 64)
353+
) :: popcnt64 x
354+
355+
let bitscan_flags is_zero_count =
356+
let l = Bil.[
357+
oF := unknown "bits" bool_t;
358+
sf := unknown "bits" bool_t;
359+
af := unknown "bits" bool_t;
360+
pf := unknown "bits" bool_t;
361+
] in
362+
if is_zero_count then l
363+
else Bil.(cf := unknown "bits" bool_t) :: l
364+
276365
let rec to_ir mode addr next ss pref has_rex has_vex =
277366
let module R = (val (vars_of_mode mode)) in
278367
let open R in
@@ -1070,31 +1159,43 @@ module ToIR = struct
10701159
Bil.Move (pf, Bil.Unknown ("PF undefined after bt", bool_t))
10711160
]
10721161
| Bs(t, dst, src, dir) ->
1073-
let t' = !!t in
1074-
let source_is_zero = tmp bool_t in
1075-
let source_is_zero_v = Bil.Var source_is_zero in
1162+
let is_zero_count = List.rev pref |> List.exists ~f:Int.((=) 0xf3) in
1163+
let width = !!t in
10761164
let src_e = op2e t src in
1077-
let bits = !!t in
1078-
let check_bit bitindex next_value =
1079-
Bil.(Ite (Extract (bitindex,bitindex,src_e) = int_exp 1 1, int_exp bitindex t', next_value))
1080-
in
1081-
let bitlist = List.init ~f:(fun x -> x) bits in
1082-
(* We are folding from right to left *)
1083-
let bitlist = match dir with
1084-
| Forward -> (* least significant first *) bitlist
1085-
| Backward -> (* most significant *) List.rev bitlist
1086-
in
1087-
let first_one = List.fold_right ~f:check_bit bitlist
1088-
~init:(Bil.Unknown("bs: destination undefined when source is zero", t)) in
1089-
[
1090-
Bil.Move (source_is_zero, Bil.(src_e = int_exp 0 t'));
1091-
assn t dst first_one;
1092-
Bil.Move (zf, Bil.Ite (source_is_zero_v, int_exp 1 1, int_exp 0 1));
1093-
]
1094-
@
1095-
let undef r =
1096-
Bil.Move (r, Bil.Unknown (Var.name r ^ " undefined after bsf", Var.typ r)) in
1097-
List.map ~f:undef [cf; oF; sf; af; pf]
1165+
let is_fwd = match dir with
1166+
| Backward -> false
1167+
| Forward -> true in
1168+
let res = tmp t in
1169+
let assn_res = Bil.(res := src_e) in
1170+
let scan_bil =
1171+
if is_fwd then match width with
1172+
| 16 -> ctz16 res
1173+
| 32 -> ctz32 res
1174+
| 64 -> ctz64 res
1175+
| _ -> disfailwith "Invalid bitscan width"
1176+
else match width with
1177+
| 16 -> clz16 res
1178+
| 32 -> clz32 res
1179+
| 64 -> clz64 res
1180+
| _ -> disfailwith "Invalid bitscan width" in
1181+
let assn_result =
1182+
let n1 = width - 1 in
1183+
assn t dst @@ if is_zero_count || is_fwd then Bil.var res
1184+
else Bil.(var res lxor int Word.(of_int n1 ~width)) in
1185+
let bil = assn_res :: scan_bil in
1186+
let bil =
1187+
if is_zero_count then bil @ Bil.[
1188+
assn_result;
1189+
cf := var res = int Word.(of_int ~width width);
1190+
zf := var res = int Word.(zero width);
1191+
]
1192+
else Bil.[
1193+
if_ (src_e = int (Word.zero width)) [
1194+
zf := int Word.b1;
1195+
assn t dst @@ unknown "bits" t;
1196+
] (bil @ Bil.[assn_result; zf := int Word.b0]);
1197+
] in
1198+
bil @ bitscan_flags is_zero_count
10981199
| Hlt -> [] (* x86 Hlt is essentially a NOP *)
10991200
| Rdtsc ->
11001201
let undef reg = assn reg32_t reg (Bil.Unknown ("rdtsc", reg32_t)) in
@@ -1233,11 +1334,17 @@ module ToIR = struct
12331334
| Popcnt(t, s, d) ->
12341335
let width = !!t in
12351336
let bits = op2e t s in
1236-
let bitvector = Array.to_list (Array.init width ~f:(fun i -> Bil.(Ite (Extract (i, i, bits), int_exp 1 width, int_exp 0 width)))) in
1237-
let count = List.reduce_exn ~f:Bil.(+) bitvector in
1238-
set_zf width bits
1239-
:: assn t d count
1240-
:: List.map ~f:(fun r -> Bil.Move (r, int_exp 0 1)) [cf; oF; sf; af; pf]
1337+
let res = tmp t in
1338+
let assn_src = Bil.(res := bits) in
1339+
let cnt = match width with
1340+
| 16 -> popcnt16 res
1341+
| 32 -> popcnt32 res
1342+
| 64 -> popcnt64 res
1343+
| _ -> disfailwith "Invalid popcnt width" in
1344+
let bil = (assn_src :: cnt) @ Bil.[assn t d (var res)] in
1345+
let flags = List.map ~f:(fun r ->
1346+
Bil.Move (r, int_exp 0 1)) [cf; oF; sf; af; pf] in
1347+
set_zf width bits :: (bil @ flags)
12411348
| Sahf ->
12421349
let assnsf = assns_lflags_to_bap in
12431350
let tah = tmp ~name:"AH" reg8_t in

0 commit comments

Comments
 (0)