Repository navigation
x64: add APX NDD lowering for unary and 32-bit integer ops #15211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,6 +103,21 @@ | |
| (if-let true (use_apx)) | ||
| (x64_addq_rvm x y)) | ||
|
|
||
| ;; APX: the same for `i32` and narrower. Note that `i8`/`i16` deliberately use | ||
| ;; the 32-bit NDD form rather than a byte/word-sized one: writing a 32-bit | ||
| ;; register breaks the false dependency on the destination's previous value, | ||
| ;; which is the same reason `x64_add_break_deps` widens these types for the | ||
| ;; legacy encoding. The upper bits of a narrow result are don't-care. | ||
| (rule iadd_apx_ndd_32 1 (lower (iadd $I32 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_addl_rvm x y)) | ||
| (rule iadd_apx_ndd_16 1 (lower (iadd $I16 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_addl_rvm (put_in_gpr x) (gpr_to_gpr_mem_32 (put_in_gpr y)))) | ||
| (rule iadd_apx_ndd_8 1 (lower (iadd $I8 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_addl_rvm (put_in_gpr x) (gpr_to_gpr_mem_32 (put_in_gpr y)))) | ||
|
Comment on lines
+114
to
+119
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might have missed this from earlier discussion, but why do these rules explicitly use |
||
|
|
||
| ;; Higher-priority cases than the previous two where a load can be sunk into | ||
| ;; the add instruction itself. Note that both operands are tested for | ||
| ;; sink-ability since addition is commutative | ||
|
|
@@ -356,6 +371,18 @@ | |
| (if-let true (use_apx)) | ||
| (x64_subq_rvm (put_in_gpr y) x)) | ||
|
|
||
| ;; APX: the same for `i32` and narrower; see `iadd_apx_ndd_32` for why `i8` and | ||
| ;; `i16` use the 32-bit form. | ||
| (rule isub_apx_ndd_32 1 (lower (isub $I32 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_subl_rvm (put_in_gpr y) x)) | ||
| (rule isub_apx_ndd_16 1 (lower (isub $I16 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_subl_rvm (put_in_gpr y) (gpr_to_gpr_mem_32 (put_in_gpr x)))) | ||
| (rule isub_apx_ndd_8 1 (lower (isub $I8 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_subl_rvm (put_in_gpr y) (gpr_to_gpr_mem_32 (put_in_gpr x)))) | ||
|
|
||
| ;; Sub two registers. | ||
| (rule -3 (lower (isub (fits_in_64 ty) x y)) | ||
| (x64_sub ty x y)) | ||
|
|
@@ -452,6 +479,18 @@ | |
| (if-let true (use_apx)) | ||
| (x64_andq_rvm x y)) | ||
|
|
||
| ;; APX: the same for `i32` and narrower; see `iadd_apx_ndd_32` for why `i8` and | ||
| ;; `i16` use the 32-bit form. | ||
| (rule band_apx_ndd_32 3 (lower (band $I32 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_andl_rvm x y)) | ||
| (rule band_apx_ndd_16 3 (lower (band $I16 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_andl_rvm (put_in_gpr x) (gpr_to_gpr_mem_32 (put_in_gpr y)))) | ||
| (rule band_apx_ndd_8 3 (lower (band $I8 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_andl_rvm (put_in_gpr x) (gpr_to_gpr_mem_32 (put_in_gpr y)))) | ||
|
|
||
| ;; And two registers. | ||
| (rule 0 (lower (band ty x y)) | ||
| (if (ty_int_ref_scalar_64 ty)) | ||
|
|
@@ -587,6 +626,18 @@ | |
| (if-let true (use_apx)) | ||
| (x64_orq_rvm x y)) | ||
|
|
||
| ;; APX: the same for `i32` and narrower; see `iadd_apx_ndd_32` for why `i8` and | ||
| ;; `i16` use the 32-bit form. | ||
| (rule bor_apx_ndd_32 3 (lower (bor $I32 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_orl_rvm x y)) | ||
| (rule bor_apx_ndd_16 3 (lower (bor $I16 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_orl_rvm (put_in_gpr x) (gpr_to_gpr_mem_32 (put_in_gpr y)))) | ||
| (rule bor_apx_ndd_8 3 (lower (bor $I8 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_orl_rvm (put_in_gpr x) (gpr_to_gpr_mem_32 (put_in_gpr y)))) | ||
|
|
||
| ;; Or two registers. | ||
| (rule 0 (lower (bor ty x y)) | ||
| (if (ty_int_ref_scalar_64 ty)) | ||
|
|
@@ -666,6 +717,18 @@ | |
| (if-let true (use_apx)) | ||
| (x64_xorq_rvm x y)) | ||
|
|
||
| ;; APX: the same for `i32` and narrower; see `iadd_apx_ndd_32` for why `i8` and | ||
| ;; `i16` use the 32-bit form. | ||
| (rule bxor_apx_ndd_32 3 (lower (bxor $I32 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_xorl_rvm x y)) | ||
| (rule bxor_apx_ndd_16 3 (lower (bxor $I16 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_xorl_rvm (put_in_gpr x) (gpr_to_gpr_mem_32 (put_in_gpr y)))) | ||
| (rule bxor_apx_ndd_8 3 (lower (bxor $I8 x y)) | ||
| (if-let true (use_apx)) | ||
| (x64_xorl_rvm (put_in_gpr x) (gpr_to_gpr_mem_32 (put_in_gpr y)))) | ||
|
|
||
| ;; Xor two registers. | ||
| (rule 0 (lower (bxor ty x y)) | ||
| (if (ty_int_ref_scalar_64 ty)) | ||
|
|
@@ -1152,6 +1215,26 @@ | |
|
|
||
| ;; `i64` and smaller. | ||
|
|
||
| ;; APX: when the `has_apx` feature is enabled, use the NDD (new-data- | ||
| ;; destination) form of `neg` for 64-bit values. The NDD form writes its result | ||
| ;; to a register encoded in the EVEX `vvvv` field, so the register allocator | ||
| ;; does not need to preserve the input with a separate move. | ||
| (rule ineg_apx_ndd 1 (lower (ineg $I64 x)) | ||
| (if-let true (use_apx)) | ||
| (x64_negq_vm x)) | ||
|
|
||
| ;; APX: the same for `i32` and narrower; see `iadd_apx_ndd_32` for why `i8` and | ||
| ;; `i16` use the 32-bit form. | ||
| (rule ineg_apx_ndd_32 1 (lower (ineg $I32 x)) | ||
| (if-let true (use_apx)) | ||
| (x64_negl_vm x)) | ||
| (rule ineg_apx_ndd_16 1 (lower (ineg $I16 x)) | ||
| (if-let true (use_apx)) | ||
| (x64_negl_vm (gpr_to_gpr_mem_32 (put_in_gpr x)))) | ||
| (rule ineg_apx_ndd_8 1 (lower (ineg $I8 x)) | ||
| (if-let true (use_apx)) | ||
| (x64_negl_vm (gpr_to_gpr_mem_32 (put_in_gpr x)))) | ||
|
|
||
| (rule -1 (lower (ineg (fits_in_64 ty) x)) | ||
| (x64_neg ty x)) | ||
|
|
||
|
|
@@ -1616,6 +1699,24 @@ | |
|
|
||
| ;; `i64` and smaller. | ||
|
|
||
| ;; APX: as with `ineg` above, prefer the NDD form of `not` for 64-bit values | ||
| ;; when APX is enabled so that the input register is not clobbered. | ||
| (rule bnot_apx_ndd 1 (lower (bnot $I64 x)) | ||
| (if-let true (use_apx)) | ||
| (x64_notq_vm x)) | ||
|
|
||
| ;; APX: the same for `i32` and narrower; see `iadd_apx_ndd_32` for why `i8` and | ||
| ;; `i16` use the 32-bit form. | ||
| (rule bnot_apx_ndd_32 1 (lower (bnot $I32 x)) | ||
| (if-let true (use_apx)) | ||
| (x64_notl_vm x)) | ||
| (rule bnot_apx_ndd_16 1 (lower (bnot $I16 x)) | ||
| (if-let true (use_apx)) | ||
| (x64_notl_vm (gpr_to_gpr_mem_32 (put_in_gpr x)))) | ||
| (rule bnot_apx_ndd_8 1 (lower (bnot $I8 x)) | ||
| (if-let true (use_apx)) | ||
| (x64_notl_vm (gpr_to_gpr_mem_32 (put_in_gpr x)))) | ||
|
Comment on lines
+1702
to
+1718
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to put these rules within the |
||
|
|
||
| (rule -2 (lower (bnot ty x)) | ||
| (if (ty_int_ref_scalar_64 ty)) | ||
| (x64_not ty x)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I'd say it's probably fine to drop all of these tests. They're pretty verbose and I don't think it's worth adding individual tests for all instructions. The "oracles" here are opaque hex strings which can't really be reviewed or verified, so I think we'll be best to lean on other testing methods like differential fuzzing (should we enable that for a disassembler that supports apx) or CI-based runtest execution (e.g. QEMU with apx emulation or intel's emulator).
Actually, along those lines, we already have some tests executed through the Intel SDE, for example this job. Does the SDE support APX? If so can you add directives in CLIF
runtests withtest x86_64 has_apxto get full execution testing for the new instructions? That's probably the best possible testing we can hope for since our disasemblers don't support APX (or it's at least not wired up)