diff --git a/lib/Arch/X86/Runtime/CMakeLists.txt b/lib/Arch/X86/Runtime/CMakeLists.txt index 15fd4cdca..a84356e60 100644 --- a/lib/Arch/X86/Runtime/CMakeLists.txt +++ b/lib/Arch/X86/Runtime/CMakeLists.txt @@ -88,6 +88,7 @@ function(add_runtime_helper target_name address_bit_size enable_avx enable_avx51 "${REMILL_LIB_DIR}/Arch/X86/Semantics/X87.cpp" "${REMILL_LIB_DIR}/Arch/X86/Semantics/COND_BR.cpp" "${REMILL_LIB_DIR}/Arch/X86/Semantics/INTERRUPT.cpp" + "${REMILL_LIB_DIR}/Arch/X86/Semantics/BMI.cpp" ) endfunction() diff --git a/lib/Arch/X86/Semantics/BITBYTE.cpp b/lib/Arch/X86/Semantics/BITBYTE.cpp index ffea5d274..9df549888 100644 --- a/lib/Arch/X86/Semantics/BITBYTE.cpp +++ b/lib/Arch/X86/Semantics/BITBYTE.cpp @@ -301,17 +301,6 @@ DEF_SEM(BSWAP_64, R64W dst, R64 src) { } #endif // 64 == ADDRESS_SIZE_BITS -template -DEF_SEM(TZCNT, D dst, S src) { - auto val = Read(src); - auto count = CountTrailingZeros(val); - ClearArithFlags(); - Write(FLAG_ZF, UCmpEq(UAnd(val, 1), 1)); - Write(FLAG_CF, ZeroFlag(val)); - WriteZExt(dst, Select(FLAG_CF, BitSizeOf(src), count)); - return memory; -} - template DEF_SEM(LZCNT, D dst, S src) { auto val = Read(src); @@ -329,9 +318,6 @@ DEF_ISEL(BSWAP_GPRv_16) = BSWAP_16; DEF_ISEL(BSWAP_GPRv_32) = BSWAP_32; IF_64BIT(DEF_ISEL(BSWAP_GPRv_64) = BSWAP_64;) -DEF_ISEL_RnW_Mn(TZCNT_GPRv_MEMv, TZCNT); -DEF_ISEL_RnW_Rn(TZCNT_GPRv_GPRv, TZCNT); - DEF_ISEL_RnW_Mn(LZCNT_GPRv_MEMv, LZCNT); DEF_ISEL_RnW_Rn(LZCNT_GPRv_GPRv, LZCNT); diff --git a/lib/Arch/X86/Semantics/BMI.cpp b/lib/Arch/X86/Semantics/BMI.cpp new file mode 100644 index 000000000..2d2c108d6 --- /dev/null +++ b/lib/Arch/X86/Semantics/BMI.cpp @@ -0,0 +1,145 @@ +/* +* Copyright (c) 2025 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace { + +template +ALWAYS_INLINE void SetFlagsBMI(State &state, T lhs, T rhs, T res) { + state.aflag.cf = false; + state.aflag.pf = __remill_undefined_8(); + state.aflag.zf = ZeroFlag(res, lhs, rhs); + state.aflag.sf = SignFlag(res, lhs, rhs); + state.aflag.of = false; + state.aflag.af = __remill_undefined_8(); +} + +template +DEF_SEM(ANDN, D dst, S1 src1, S2 src2) { + auto lhs = Read(src1); + auto rhs = Read(src2); + auto res = UAnd(UNot(lhs), rhs); + WriteZExt(dst, res); + SetFlagsBMI(state, lhs, rhs, res); + return memory; +} + +template +DEF_SEM(BEXTR, D dst, S1 src1, S2 src2) { + auto source = Read(src1); + auto control = Read(src2); + + // Extract start position from bits [7:0] + auto start = ZExtTo(UAnd(control, Literal(0xFF))); + // Extract length from bits [15:8] + auto length = ZExtTo(UAnd(UShr(control, Literal(8)), Literal(0xFF))); + + // Constrain start and length to operand size to avoid undefined behavior + start = URem(start, BitSizeOf(src1)); + length = URem(length, BitSizeOf(src1)); + + // Extract bits: (source >> start) & ((1 << length) - 1) + auto shifted = UShr(source, start); + auto mask = USub(UShl(Literal(1), length), Literal(1)); + auto result = UAnd(shifted, mask); + + WriteZExt(dst, result); + + // Set flags according to Intel specification + Write(FLAG_ZF, ZeroFlag(result, source, control)); + Write(FLAG_OF, false); + Write(FLAG_CF, false); + Write(FLAG_AF, __remill_undefined_8()); + Write(FLAG_SF, __remill_undefined_8()); + Write(FLAG_PF, __remill_undefined_8()); + + return memory; +} + +template +DEF_SEM(BLSI, D dst, S src) { + auto val = Read(src); + auto res = UAnd(UNeg(val), val); + WriteZExt(dst, res); + SetFlagsBMI(state, val, val, res); + Write(FLAG_CF, UCmpNeq(val, 0)); + return memory; +} + +template +DEF_SEM(BLSMSK, D dst, S src) { + auto val = Read(src); + auto res = UXor(USub(val, Literal(1)), val); + WriteZExt(dst, res); + SetFlagsBMI(state, val, val, res); + Write(FLAG_CF, UCmpEq(val, 0)); + Write(FLAG_ZF, false); + return memory; +} + +template +DEF_SEM(BLSR, D dst, S src) { + auto val = Read(src); + auto res = UAnd(USub(val, Literal(1)), val); + WriteZExt(dst, res); + SetFlagsBMI(state, val, val, res); + Write(FLAG_CF, UCmpEq(val, 0)); + return memory; +} + +template +DEF_SEM(TZCNT, D dst, S src) { + auto val = Read(src); + auto count = CountTrailingZeros(val); + ClearArithFlags(); + Write(FLAG_ZF, UCmpEq(UAnd(val, Literal(1)), 1)); + Write(FLAG_CF, ZeroFlag(val)); + WriteZExt(dst, Select(FLAG_CF, BitSizeOf(src), count)); + return memory; +} + +} // namespace + +DEF_ISEL(ANDN_VGPR32d_VGPR32d_VGPR32d) = ANDN; +DEF_ISEL(ANDN_VGPR32d_VGPR32d_MEMd) = ANDN; +IF_64BIT(DEF_ISEL(ANDN_VGPR64q_VGPR64q_VGPR64q) = ANDN;) +IF_64BIT(DEF_ISEL(ANDN_VGPR64q_VGPR64q_MEMq) = ANDN;) + +DEF_ISEL(BEXTR_VGPR32d_VGPR32d_VGPR32d) = BEXTR; +DEF_ISEL(BEXTR_VGPR32d_MEMd_VGPR32d) = BEXTR; +IF_64BIT(DEF_ISEL(BEXTR_VGPR64q_VGPR64q_VGPR64q) = BEXTR;) +IF_64BIT(DEF_ISEL(BEXTR_VGPR64q_MEMq_VGPR64q) = BEXTR;) + +DEF_ISEL(BLSI_VGPR32d_VGPR32d) = BLSI; +DEF_ISEL(BLSI_VGPR32d_MEMd) = BLSI; +IF_64BIT(DEF_ISEL(BLSI_VGPR64q_VGPR64q) = BLSI;) +IF_64BIT(DEF_ISEL(BLSI_VGPR64q_MEMq) = BLSI;) + +DEF_ISEL(BLSMSK_VGPR32d_VGPR32d) = BLSMSK; +DEF_ISEL(BLSMSK_VGPR32d_MEMd) = BLSMSK; +IF_64BIT(DEF_ISEL(BLSMSK_VGPR64q_VGPR64q) = BLSMSK;) +IF_64BIT(DEF_ISEL(BLSMSK_VGPR64q_MEMq) = BLSMSK;) + +DEF_ISEL(BLSR_VGPR32d_VGPR32d) = BLSR; +DEF_ISEL(BLSR_VGPR32d_MEMd) = BLSR; +IF_64BIT(DEF_ISEL(BLSR_VGPR64q_VGPR64q) = BLSR;) +IF_64BIT(DEF_ISEL(BLSR_VGPR64q_MEMq) = BLSR;) + +DEF_ISEL(TZCNT_GPRv_GPRv_16) = TZCNT; +DEF_ISEL(TZCNT_GPRv_MEMv_16) = TZCNT; +DEF_ISEL(TZCNT_GPRv_GPRv_32) = TZCNT; +DEF_ISEL(TZCNT_GPRv_MEMv_32) = TZCNT; +IF_64BIT(DEF_ISEL(TZCNT_GPRv_GPRv_64) = TZCNT;) +IF_64BIT(DEF_ISEL(TZCNT_GPRv_MEMv_64) = TZCNT;) diff --git a/tests/X86/BMI/ANDN.S b/tests/X86/BMI/ANDN.S new file mode 100644 index 000000000..8c785ef69 --- /dev/null +++ b/tests/X86/BMI/ANDN.S @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if HAS_FEATURE_AVX + +TEST_BEGIN(ANDNr32r32r32_basic, 2) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0x00000000, 0x00000000, // (~0) & 0 = 0xFFFFFFFF & 0 = 0 + 0x00000000, 0xFFFFFFFF, // (~0) & 0xFFFFFFFF = 0xFFFFFFFF (all bits set) + 0xFFFFFFFF, 0x00000000, // (~0xFFFFFFFF) & 0 = 0 (zero result, ZF=1) + 0xFFFFFFFF, 0xFFFFFFFF, // (~0xFFFFFFFF) & 0xFFFFFFFF = 0 (ZF=1) + 0xAAAAAAAA, 0x55555555, // (~0xAAAAAAAA) & 0x55555555 = 0x55555555 & 0x55555555 + 0x12345678, 0x9ABCDEF0, // Mixed bits + 0x80000000, 0x80000000, // Sign bit: (~0x80000000) & 0x80000000 = 0 + 0x7FFFFFFF, 0xFFFFFFFF) // (~0x7FFFFFFF) & 0xFFFFFFFF = 0x80000000 (SF=1) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz andn_nop1 + + // ANDN supported, proceed with test: + mov eax, ARG1_32 + mov edx, ARG2_32 + andn ecx, eax, edx + +andn_nop1: nop + +TEST_END + +TEST_BEGIN_64(ANDNr64r64r64_basic, 2) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0xFFFFFFFFFFFFFFFF, + 0xFFFFFFFFFFFFFFFF, 0x0000000000000000, // Zero result + 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, // Zero result + 0xAAAAAAAAAAAAAAAA, 0x5555555555555555, + 0x123456789ABCDEF0, 0xFEDCBA9876543210, + 0x8000000000000000, 0x8000000000000000, // Sign bit + 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF) // Result: 0x8000000000000000 (SF=1) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz andn_nop2 + + // ANDN supported, proceed with test: + mov rax, ARG1_64 + mov rdx, ARG2_64 + andn rcx, rax, rdx + +andn_nop2: nop + +TEST_END_64 + +TEST_BEGIN(ANDNr32r32m32, 2) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0xFFFFFFFF, 0x12345678, + 0x00000000, 0xABCDEF01, + 0x55555555, 0xAAAAAAAA) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz andn_nop3 + + // ANDN supported, proceed with test: + push ARG2_64 + mov eax, ARG1_32 + andn ecx, eax, DWORD PTR [rsp] + add rsp, 8 + +andn_nop3: nop + +TEST_END + +TEST_BEGIN_64(ANDNr64r64m64, 2) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0xFFFFFFFFFFFFFFFF, 0x123456789ABCDEF0, + 0x0000000000000000, 0xFEDCBA9876543210, + 0xAAAAAAAAAAAAAAAA, 0x5555555555555555) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz andn_nop4 + + // ANDN supported, proceed with test: + push ARG2_64 + mov rax, ARG1_64 + andn rcx, rax, QWORD PTR [rsp] + add rsp, 8 + +andn_nop4: nop + +TEST_END_64 + +#endif // HAS_FEATURE_AVX diff --git a/tests/X86/BMI/BEXTR.S b/tests/X86/BMI/BEXTR.S new file mode 100644 index 000000000..444b71d76 --- /dev/null +++ b/tests/X86/BMI/BEXTR.S @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2025 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if HAS_FEATURE_AVX + +TEST_BEGIN(BEXTRr32r32r32_basic, 2) +TEST_IGNORE_FLAGS(AF SF PF) +TEST_INPUTS( + 0xFFFFFFFF, 0x0800, // Extract 8 bits starting at position 0 + 0x12345678, 0x0808, // Extract 8 bits starting at position 8 + 0xABCDEF01, 0x1000, // Extract 16 bits starting at position 0 + 0xDEADBEEF, 0x0810, // Extract 8 bits starting at position 16 + 0x87654321, 0x2000, // Extract 32 bits starting at position 0 + 0xFFFFFFFF, 0x0000, // Extract 0 bits (length=0) + 0x12345678, 0x0100, // Extract 1 bit at position 0 + 0xAAAAAAAA, 0x0401) // Extract 4 bits starting at position 1 + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz bextr_nop1 + + // BEXTR supported, proceed with test: + mov eax, ARG1_32 + mov edx, ARG2_32 + bextr ecx, eax, edx + +bextr_nop1: nop + +TEST_END + +TEST_BEGIN(BEXTRr32r32r32_zero, 2) +TEST_IGNORE_FLAGS(AF SF PF) +TEST_INPUTS( + 0x00000000, 0x0800, // Extract from zero source + 0xFFFFFFFF, 0x081F, // Extract 8 bits at position 31 (should be zero) + 0x12345678, 0x0820) // Extract 8 bits at position 32+ (should be zero) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz bextr_nop2 + + // BEXTR supported, proceed with test: + mov eax, ARG1_32 + mov edx, ARG2_32 + bextr ecx, eax, edx + +bextr_nop2: nop + +TEST_END + +TEST_BEGIN_64(BEXTRr64r64r64_basic, 2) +TEST_IGNORE_FLAGS(AF SF PF) +TEST_INPUTS( + 0xFFFFFFFFFFFFFFFF, 0x0800, // Extract 8 bits starting at position 0 + 0x123456789ABCDEF0, 0x0808, // Extract 8 bits starting at position 8 + 0xDEADBEEFCAFEBABE, 0x1000, // Extract 16 bits starting at position 0 + 0x0123456789ABCDEF, 0x0820, // Extract 8 bits starting at position 32 + 0xFFFFFFFF00000000, 0x2020, // Extract 32 bits starting at position 32 + 0xAAAAAAAAAAAAAAAA, 0x4000, // Extract 64 bits starting at position 0 + 0x123456789ABCDEF0, 0x0000, // Extract 0 bits (length=0) + 0x8000000000000000, 0x013F) // Extract 1 bit at position 63 + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz bextr_nop3 + + // BEXTR supported, proceed with test: + mov rax, ARG1_64 + mov rdx, ARG2_64 + bextr rcx, rax, rdx + +bextr_nop3: nop + +TEST_END_64 + +TEST_BEGIN_64(BEXTRr64r64r64_zero, 2) +TEST_IGNORE_FLAGS(AF SF PF) +TEST_INPUTS( + 0x0000000000000000, 0x0800, // Extract from zero source + 0xFFFFFFFFFFFFFFFF, 0x083F, // Extract 8 bits at position 63 (should be 1 bit) + 0x123456789ABCDEF0, 0x0840) // Extract 8 bits at position 64+ (should be zero) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz bextr_nop4 + + // BEXTR supported, proceed with test: + mov rax, ARG1_64 + mov rdx, ARG2_64 + bextr rcx, rax, rdx + +bextr_nop4: nop + +TEST_END_64 + +TEST_BEGIN(BEXTRr32m32r32, 2) +TEST_IGNORE_FLAGS(AF SF PF) +TEST_INPUTS( + 0xDEADBEEF, 0x0800, + 0x12345678, 0x0C10, + 0xFFFFFFFF, 0x1000) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz bextr_nop5 + + // BEXTR supported, proceed with test: + push ARG1_64 + mov edx, ARG2_32 + bextr ecx, DWORD PTR [rsp], edx + add rsp, 8 + +bextr_nop5: nop + +TEST_END + +TEST_BEGIN_64(BEXTRr64m64r64, 2) +TEST_IGNORE_FLAGS(AF SF PF) +TEST_INPUTS( + 0xDEADBEEFCAFEBABE, 0x0800, + 0x123456789ABCDEF0, 0x1010, + 0xFFFFFFFFFFFFFFFF, 0x2000) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz bextr_nop6 + + // BEXTR supported, proceed with test: + push ARG1_64 + mov rdx, ARG2_64 + bextr rcx, QWORD PTR [rsp], rdx + add rsp, 8 + +bextr_nop6: nop + +TEST_END_64 + +#endif // HAS_FEATURE_AVX diff --git a/tests/X86/BMI/BLSI.S b/tests/X86/BMI/BLSI.S new file mode 100644 index 000000000..ab4f217d4 --- /dev/null +++ b/tests/X86/BMI/BLSI.S @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2025 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if HAS_FEATURE_AVX + +TEST_BEGIN(BLSIr32r32_zero, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS(0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsi_nop1 + + // BLSI supported, proceed with test: + mov eax, ARG1_32 + blsi edx, eax + +blsi_nop1: nop + +TEST_END + +TEST_BEGIN(BLSIr32r32_basic, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 1, // Result: 1, CF=1 + 2, // Result: 2, CF=1 + 3, // Result: 1 (lowest bit), CF=1 + 0x80000000, // MSB set: result=0x80000000 (SF=1), CF=1 + 0x80000001, // MSB and LSB: result=1, CF=1 + 0xFFFFFFFF, // All bits: result=1 (lowest), CF=1 + 0xFFFFFFFE, // All but LSB: result=2, CF=1 + 0x12345678, // Result: 8 (bit 3), CF=1 + 0xAAAAAAAA, // Alternating bits: result=2, CF=1 + 0x55555555) // Alternating bits: result=1, CF=1 + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsi_nop2 + + // BLSI supported, proceed with test: + mov eax, ARG1_32 + blsi edx, eax + +blsi_nop2: nop + +TEST_END + +TEST_BEGIN(BLSIr32r32_powers_of_two, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0x00000001, // 2^0 + 0x00000002, // 2^1 + 0x00000004, // 2^2 + 0x00000100, // 2^8 + 0x00010000, // 2^16 + 0x40000000, // 2^30 + 0x80000000) // 2^31 (sign bit) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsi_nop3 + + // BLSI supported, proceed with test: + mov eax, ARG1_32 + blsi edx, eax + +blsi_nop3: nop + +TEST_END + +TEST_BEGIN_64(BLSIr64r64_zero, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS(0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsi_nop4 + + // BLSI supported, proceed with test: + mov rax, ARG1_64 + blsi rdx, rax + +blsi_nop4: nop + +TEST_END_64 + +TEST_BEGIN_64(BLSIr64r64_basic, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 1, + 2, + 3, + 0x8000000000000000, // MSB: result=0x8000000000000000 (SF=1) + 0x8000000000000001, // MSB and LSB: result=1 + 0xFFFFFFFFFFFFFFFF, // All bits: result=1 + 0xFFFFFFFFFFFFFFFE, // All but LSB: result=2 + 0x123456789ABCDEF0, // Result: 0x10 (bit 4) + 0xAAAAAAAAAAAAAAAA, // Result: 2 + 0x5555555555555555) // Result: 1 + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsi_nop5 + + // BLSI supported, proceed with test: + mov rax, ARG1_64 + blsi rdx, rax + +blsi_nop5: nop + +TEST_END_64 + +TEST_BEGIN(BLSIr32m32, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0, + 0xFFFFFFFF, + 0x12345678) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsi_nop6 + + // BLSI supported, proceed with test: + push ARG1_64 + blsi edx, DWORD PTR [rsp] + add rsp, 8 + +blsi_nop6: nop + +TEST_END + +TEST_BEGIN_64(BLSIr64m64, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0, + 0xFFFFFFFFFFFFFFFF, + 0x123456789ABCDEF0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsi_nop7 + + // BLSI supported, proceed with test: + push ARG1_64 + blsi rdx, QWORD PTR [rsp] + add rsp, 8 + +blsi_nop7: nop + +TEST_END_64 + +#endif // HAS_FEATURE_AVX diff --git a/tests/X86/BMI/BLSMSK.S b/tests/X86/BMI/BLSMSK.S new file mode 100644 index 000000000..48ebef1b2 --- /dev/null +++ b/tests/X86/BMI/BLSMSK.S @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2025 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if HAS_FEATURE_AVX + +TEST_BEGIN(BLSMSKr32r32_zero, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS(0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsmsk_nop1 + + // BLMSK supported, proceed with test: + mov eax, ARG1_32 + blsmsk edx, eax + +blsmsk_nop1: nop + +TEST_END + +TEST_BEGIN(BLSMSKr32r32_basic, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 1, // (0) ^ 1 = 1, CF=0 + 2, // (1) ^ 2 = 3, CF=0 + 3, // (2) ^ 3 = 1, CF=0 + 4, // (3) ^ 4 = 7, CF=0 + 0x80000000, // Result: 0x7FFFFFFF ^ 0x80000000 = 0xFFFFFFFF (SF=1), CF=0 + 0xFFFFFFFF, // (0xFFFFFFFE) ^ 0xFFFFFFFF = 1, CF=0 + 0xFFFFFFFE, // Result: 3, CF=0 + 0x12345678, // Result based on bit 3 (LSB at position 3) + 0xAAAAAAAA, // Alternating bits, LSB at position 1 + 0x55555555) // Alternating bits, LSB at position 0 + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsmsk_nop2 + + // BLSMSK supported, proceed with test: + mov eax, ARG1_32 + blsmsk edx, eax + +blsmsk_nop2: nop + +TEST_END + +TEST_BEGIN(BLSMSKr32r32_powers_of_two, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0x00000001, // Result: 1 + 0x00000002, // Result: 3 + 0x00000004, // Result: 7 + 0x00000008, // Result: 15 + 0x00000100, // Result: 0xFF + 0x00010000, // Result: 0xFFFF + 0x40000000, // Result: 0x7FFFFFFF + 0x80000000) // Result: 0xFFFFFFFF (all bits, SF=1) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsmsk_nop3 + + // BLSMSK supported, proceed with test: + mov eax, ARG1_32 + blsmsk edx, eax + +blsmsk_nop3: nop + +TEST_END + +TEST_BEGIN_64(BLSMSKr64r64_zero, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS(0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsmsk_nop4 + + // BLSMSK supported, proceed with test: + mov rax, ARG1_64 + blsmsk rdx, rax + +blsmsk_nop4: nop + +TEST_END_64 + +TEST_BEGIN_64(BLSMSKr64r64_basic, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 1, + 2, + 3, + 0x8000000000000000, // Result: 0xFFFFFFFFFFFFFFFF (SF=1) + 0xFFFFFFFFFFFFFFFF, // Result: 1 + 0xFFFFFFFFFFFFFFFE, // Result: 3 + 0x123456789ABCDEF0, // Result based on bit 4 + 0xAAAAAAAAAAAAAAAA, // Result: 3 + 0x5555555555555555) // Result: 1 + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsmsk_nop5 + + // BLSMSK supported, proceed with test: + mov rax, ARG1_64 + blsmsk rdx, rax + +blsmsk_nop5: nop + +TEST_END_64 + +TEST_BEGIN_64(BLSMSKr64r64_powers_of_two, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0x0000000000000001, // Result: 1 + 0x0000000000000002, // Result: 3 + 0x0000000000000100, // Result: 0xFF + 0x0000000000010000, // Result: 0xFFFF + 0x0000000100000000, // Result: 0xFFFFFFFF + 0x4000000000000000, // Result: 0x3FFFFFFFFFFFFFFF + 0x8000000000000000) // Result: 0xFFFFFFFFFFFFFFFF (SF=1) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsmsk_nop6 + + // BLSMSK supported, proceed with test: + mov rax, ARG1_64 + blsmsk rdx, rax + +blsmsk_nop6: nop + +TEST_END_64 + +TEST_BEGIN(BLSMSKr32m32, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0, + 0x00000001, + 0x12345678) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsmsk_nop7 + + // BLSMSK supported, proceed with test: + push ARG1_64 + blsmsk edx, DWORD PTR [rsp] + add rsp, 8 + +blsmsk_nop7: nop + +TEST_END + +TEST_BEGIN_64(BLSMSKr64m64, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0, + 0x0000000000000001, + 0x123456789ABCDEF0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsmsk_nop8 + + // BLSMSK supported, proceed with test: + push ARG1_64 + blsmsk rdx, QWORD PTR [rsp] + add rsp, 8 + +blsmsk_nop8: nop + +TEST_END_64 + +#endif // HAS_FEATURE_AVX diff --git a/tests/X86/BMI/BLSR.S b/tests/X86/BMI/BLSR.S new file mode 100644 index 000000000..4ff002fac --- /dev/null +++ b/tests/X86/BMI/BLSR.S @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2025 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if HAS_FEATURE_AVX + +TEST_BEGIN(BLSRr32r32_zero, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS(0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsr_nop1 + + // BLSR supported, proceed with test: + mov eax, ARG1_32 + blsr edx, eax + +blsr_nop1: nop + +TEST_END + +TEST_BEGIN(BLSRr32r32_single_bit, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 1, // Result: 0, CF=0, ZF=1 + 2, // Result: 0, CF=0, ZF=1 + 4, // Result: 0, CF=0, ZF=1 + 8, // Result: 0, CF=0, ZF=1 + 0x00000100, // Result: 0, CF=0, ZF=1 + 0x00010000, // Result: 0, CF=0, ZF=1 + 0x40000000, // Result: 0, CF=0, ZF=1 + 0x80000000) // Result: 0, CF=0, ZF=1 (MSB) + + // Check CPU for BMI1 support + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsr_nop2 + + mov eax, ARG1_32 + blsr edx, eax + +blsr_nop2: nop + +TEST_END + +TEST_BEGIN(BLSRr32r32_multiple_bits, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 3, // (2) & 3 = 2, CF=0 + 5, // (4) & 5 = 4, CF=0 + 7, // (6) & 7 = 6, CF=0 + 0xFFFFFFFF, // (0xFFFFFFFE) & 0xFFFFFFFF = 0xFFFFFFFE (SF=1), CF=0 + 0xFFFFFFFE, // Result: 0xFFFFFFFC (SF=1), CF=0 + 0x12345678, // Clear bit 3 (LSB) + 0xAAAAAAAA, // Clear bit 1, result: 0xAAAAAAA8 (SF=1) + 0x55555555, // Clear bit 0, result: 0x55555554 + 0x80000001, // Clear bit 0, result: 0x80000000 (SF=1) + 0x7FFFFFFF) // Clear bit 0, result: 0x7FFFFFFE + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsr_nop3 + + // BLSR supported, proceed with test: + mov eax, ARG1_32 + blsr edx, eax + +blsr_nop3: nop + +TEST_END + +TEST_BEGIN_64(BLSRr64r64_zero, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS(0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsr_nop4 + + // BLSR supported, proceed with test: + mov rax, ARG1_64 + blsr rdx, rax + +blsr_nop4: nop + +TEST_END_64 + +TEST_BEGIN_64(BLSRr64r64_single_bit, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 1, + 2, + 0x0000000000000100, + 0x0000000000010000, + 0x0000000100000000, + 0x4000000000000000, + 0x8000000000000000) // MSB + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsr_nop5 + + // BLSR supported, proceed with test: + mov rax, ARG1_64 + blsr rdx, rax + +blsr_nop5: nop + +TEST_END_64 + +TEST_BEGIN_64(BLSRr64r64_multiple_bits, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 3, + 5, + 7, + 0xFFFFFFFFFFFFFFFF, // Result: 0xFFFFFFFFFFFFFFFE (SF=1) + 0xFFFFFFFFFFFFFFFE, // Result: 0xFFFFFFFFFFFFFFFC (SF=1) + 0x123456789ABCDEF0, // Clear bit 4 + 0xAAAAAAAAAAAAAAAA, // Clear bit 1, result: 0xAAAAAAAAAAAAAAA8 (SF=1) + 0x5555555555555555, // Clear bit 0, result: 0x5555555555555554 + 0x8000000000000001, // Clear bit 0, result: 0x8000000000000000 (SF=1) + 0x7FFFFFFFFFFFFFFF) // Clear bit 0, result: 0x7FFFFFFFFFFFFFFE + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsr_nop6 + + // BLSR supported, proceed with test: + mov rax, ARG1_64 + blsr rdx, rax + +blsr_nop6: nop + +TEST_END_64 + +TEST_BEGIN(BLSRr32m32, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0, + 1, + 0xFFFFFFFF, + 0x12345678) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsr_nop7 + + // BLSR supported, proceed with test: + push ARG1_64 + blsr edx, DWORD PTR [rsp] + add rsp, 8 + +blsr_nop7: nop + +TEST_END + +TEST_BEGIN_64(BLSRr64m64, 1) +TEST_IGNORE_FLAGS(AF PF) +TEST_INPUTS( + 0, + 1, + 0xFFFFFFFFFFFFFFFF, + 0x123456789ABCDEF0) + + // Check CPU for BMI1 support (cpuid fn 0x00000007, bit 3 of ebx) + mov eax, 0x00000007 + mov ecx, 0x0 + cpuid + and ebx, 0x00000008 + jz blsr_nop8 + + // BLSR supported, proceed with test: + push ARG1_64 + blsr rdx, QWORD PTR [rsp] + add rsp, 8 + +blsr_nop8: nop + +TEST_END_64 + +#endif // HAS_FEATURE_AVX diff --git a/tests/X86/BITBYTE/TZCNT.S b/tests/X86/BMI/TZCNT.S similarity index 78% rename from tests/X86/BITBYTE/TZCNT.S rename to tests/X86/BMI/TZCNT.S index b137c7c71..78b54d50a 100644 --- a/tests/X86/BITBYTE/TZCNT.S +++ b/tests/X86/BMI/TZCNT.S @@ -25,11 +25,10 @@ TEST_INPUTS( 0x0080, 0x0800) - // check CPU for TZCNT support first. (cpuid fn 0x00000007, bit 3 of ebx) - mov eax, 0x00000007 - mov ecx, 0x0 + // Check CPU for ABM support (cpuid fn 0x80000001, bit 5 of ecx) + mov eax, 0x80000001 cpuid - and ebx, 0x00000008 + and ecx, 0x00000020 jz tzcnt_nop1 // TZCNT supported, proceed with test: @@ -51,11 +50,10 @@ TEST_INPUTS( 0x00000080, 0x08000000) - // check CPU for TZCNT support first. (cpuid fn 0x00000007, bit 3 of ebx) - mov eax, 0x00000007 - mov ecx, 0x0 + // Check CPU for ABM support (cpuid fn 0x80000001, bit 5 of ecx) + mov eax, 0x80000001 cpuid - and ebx, 0x00000008 + and ecx, 0x00000020 jz tzcnt_nop2 // TZCNT supported, proceed with test: @@ -77,11 +75,10 @@ TEST_INPUTS( 0x0000000000000080, 0x0800000000000000) - // check CPU for TZCNT support first. (cpuid fn 0x00000007, bit 3 of ebx) - mov eax, 0x00000007 - mov ecx, 0x0 + // Check CPU for ABM support (cpuid fn 0x80000001, bit 5 of ecx) + mov eax, 0x80000001 cpuid - and ebx, 0x00000008 + and ecx, 0x00000020 jz tzcnt_nop3 // TZCNT supported, proceed with test: diff --git a/tests/X86/Tests.S b/tests/X86/Tests.S index a00be4892..b881753fe 100644 --- a/tests/X86/Tests.S +++ b/tests/X86/Tests.S @@ -402,7 +402,13 @@ SYMBOL(__x86_test_table_begin): #include "tests/X86/BITBYTE/BTS.S" #include "tests/X86/BITBYTE/LZCNT.S" #include "tests/X86/BITBYTE/SETcc.S" -#include "tests/X86/BITBYTE/TZCNT.S" + +#include "tests/X86/BMI/ANDN.S" +#include "tests/X86/BMI/BEXTR.S" +#include "tests/X86/BMI/BLSI.S" +#include "tests/X86/BMI/BLSMSK.S" +#include "tests/X86/BMI/BLSR.S" +#include "tests/X86/BMI/TZCNT.S" #include "tests/X86/CMOV/CMOVB.S" #include "tests/X86/CMOV/CMOVBE.S"