Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/Arch/X86/Runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
14 changes: 0 additions & 14 deletions lib/Arch/X86/Semantics/BITBYTE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,17 +301,6 @@ DEF_SEM(BSWAP_64, R64W dst, R64 src) {
}
#endif // 64 == ADDRESS_SIZE_BITS

template <typename D, typename S>
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 <typename D, typename S>
DEF_SEM(LZCNT, D dst, S src) {
auto val = Read(src);
Expand All @@ -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);

Expand Down
145 changes: 145 additions & 0 deletions lib/Arch/X86/Semantics/BMI.cpp
Original file line number Diff line number Diff line change
@@ -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 <typename T>
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 <typename D, typename S1, typename S2>
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 <typename D, typename S1, typename S2>
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<S1>(UAnd(control, Literal<S2>(0xFF)));
// Extract length from bits [15:8]
auto length = ZExtTo<S1>(UAnd(UShr(control, Literal<S2>(8)), Literal<S2>(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<S1>(1), length), Literal<S1>(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 <typename D, typename S>
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 <typename D, typename S>
DEF_SEM(BLSMSK, D dst, S src) {
auto val = Read(src);
auto res = UXor(USub(val, Literal<S>(1)), val);
WriteZExt(dst, res);
SetFlagsBMI(state, val, val, res);
Write(FLAG_CF, UCmpEq(val, 0));
Write(FLAG_ZF, false);
return memory;
}

template <typename D, typename S>
DEF_SEM(BLSR, D dst, S src) {
auto val = Read(src);
auto res = UAnd(USub(val, Literal<S>(1)), val);
WriteZExt(dst, res);
SetFlagsBMI(state, val, val, res);
Write(FLAG_CF, UCmpEq(val, 0));
return memory;
}

template <typename D, typename S>
DEF_SEM(TZCNT, D dst, S src) {
auto val = Read(src);
auto count = CountTrailingZeros(val);
ClearArithFlags();
Write(FLAG_ZF, UCmpEq(UAnd(val, Literal<S>(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<R32W, R32, R32>;
DEF_ISEL(ANDN_VGPR32d_VGPR32d_MEMd) = ANDN<R32W, R32, M32>;
IF_64BIT(DEF_ISEL(ANDN_VGPR64q_VGPR64q_VGPR64q) = ANDN<R64W, R64, R64>;)
IF_64BIT(DEF_ISEL(ANDN_VGPR64q_VGPR64q_MEMq) = ANDN<R64W, R64, M64>;)

DEF_ISEL(BEXTR_VGPR32d_VGPR32d_VGPR32d) = BEXTR<R32W, R32, R32>;
DEF_ISEL(BEXTR_VGPR32d_MEMd_VGPR32d) = BEXTR<R32W, M32, R32>;
IF_64BIT(DEF_ISEL(BEXTR_VGPR64q_VGPR64q_VGPR64q) = BEXTR<R64W, R64, R64>;)
IF_64BIT(DEF_ISEL(BEXTR_VGPR64q_MEMq_VGPR64q) = BEXTR<R64W, M64, R64>;)

DEF_ISEL(BLSI_VGPR32d_VGPR32d) = BLSI<R32W, R32>;
DEF_ISEL(BLSI_VGPR32d_MEMd) = BLSI<R32W, M32>;
IF_64BIT(DEF_ISEL(BLSI_VGPR64q_VGPR64q) = BLSI<R64W, R64>;)
IF_64BIT(DEF_ISEL(BLSI_VGPR64q_MEMq) = BLSI<R64W, M64>;)

DEF_ISEL(BLSMSK_VGPR32d_VGPR32d) = BLSMSK<R32W, R32>;
DEF_ISEL(BLSMSK_VGPR32d_MEMd) = BLSMSK<R32W, M32>;
IF_64BIT(DEF_ISEL(BLSMSK_VGPR64q_VGPR64q) = BLSMSK<R64W, R64>;)
IF_64BIT(DEF_ISEL(BLSMSK_VGPR64q_MEMq) = BLSMSK<R64W, M64>;)

DEF_ISEL(BLSR_VGPR32d_VGPR32d) = BLSR<R32W, R32>;
DEF_ISEL(BLSR_VGPR32d_MEMd) = BLSR<R32W, M32>;
IF_64BIT(DEF_ISEL(BLSR_VGPR64q_VGPR64q) = BLSR<R64W, R64>;)
IF_64BIT(DEF_ISEL(BLSR_VGPR64q_MEMq) = BLSR<R64W, M64>;)

DEF_ISEL(TZCNT_GPRv_GPRv_16) = TZCNT<R16W, R16>;
DEF_ISEL(TZCNT_GPRv_MEMv_16) = TZCNT<R16W, M16>;
DEF_ISEL(TZCNT_GPRv_GPRv_32) = TZCNT<R32W, R32>;
DEF_ISEL(TZCNT_GPRv_MEMv_32) = TZCNT<R32W, M32>;
IF_64BIT(DEF_ISEL(TZCNT_GPRv_GPRv_64) = TZCNT<R64W, R64>;)
IF_64BIT(DEF_ISEL(TZCNT_GPRv_MEMv_64) = TZCNT<R64W, M64>;)
123 changes: 123 additions & 0 deletions tests/X86/BMI/ANDN.S
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading